I want to know about spring hateoas - spring-boot

I am working on spring boot. I don't know what is spring hateoas why we go for spring hateoas.
#RequestMapping(value= "/accounts/{id}/{userId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Resource<AccountHolder> findAccountHolderById(#PathVariable("id") Long id, #PathVariable("userId") int i) {
logger.info("accounts findAccountHolderById() invoked: " + id);
Account account = accountRepository.getAccount(id.toString());
AccountHolder accountHolder = account.getAccountHolder();
Resource<AccountHolder> resource = new Resource<AccountHolder>(accountHolder);
resource.add(linkTo(methodOn(AccountController.class).byId(account.getAccountId())).withRel("account"));
logger.info("accounts findAccountHolderById() found: " + account);
return resource;
}

HATEOAS means that a REST webservice not only provides the answer you asked for (e.g. the account) but also links to related data like the customer or subaccounts of that account. You can also provide links to actions like "disable account". That way the clients can navigate through the data more easily.
See https://spring.io/understanding/HATEOAS or https://en.wikipedia.org/wiki/HATEOAS
P.S.: When copy & pasting code, use the "{}" symbol in the edit box to format it correctly.

Related

Spring boot Swagger annotation to link another end-point in swagger ui api doc

I have a microservice project built using spring boot, which exposes an endpoint which say is version1.
Now, I need to add a link to the v2 endpoint and deprecate v1, so in Swagger UI the v1 will be linked in the deprecated section.
I deprecate the version1 using #Deprecate java annotation, but not sure if there is an annotation that i can use to link the new endpoint on version1 so the Swagger UI api doc will display the link.
say,
# deprecated - refer the new end-point link
my-app/endpoint/v1
# new version of end-point
my-app/endpoint/v2
This might not be the perfect answer but closest to what I needed.
When using V1 and V2, it is better to use the approach suggested by #meridbt
From the Openapi documentation, Links server different purpose. If the existing endpoint is using another endpoint to fetch info, we use link.
For my scenario, i used the Links option in the #Operation annoation
#Operation(summary = "Wished hello", operationId = "requestMessage", responses = {
#ApiResponse(responseCode = "200", description = "OK", links = {
#Link(name = "hello", operationId = "SayHello", parameters = {
#LinkParameter(name = "message", expression = "$request.query.message"),
})
})
})
#Deprecated(since = "0.0.1", forRemoval = true)
#GetMapping("/greet/{message}")
public String getGreetings(#PathVariable String message) {
return "hello World";
}
/**
* Below is the new endpoint, which needs to be used
*/
#GetMapping("/hello")
public String getHello(String message) {
return "Hello from the serever";
}

Adding links to every record with Spring REST + MongoDB

I'm trying to add links to every record in my databse, as I am trying to implement the HATEOAS concept. However, I have been experiencing some trouble with this. I tried following this guide https://spring.io/guides/gs/rest-hateoas/. But with no success. How and where should I write the code for adding links? Because it doesn't seem to work when I try to write it in my controller method because withSelfRel() is undefined.
Basically I'm trying to add a link to every account made in my database.
//Create account
#RequestMapping(value="/accounts", method = RequestMethod.POST)
public ResponseEntity<?> accountInsert(#RequestBody Account account) {
account = new Account(account.getFirstName(), account.getLastName(), account.getEmail(), account.getPassword(), account.getBirthDate(), account.getActivities(), account.getFriends());
accountRepository.save(account);
//account.add(linkTo(methodOn(AccountController.class, accountInsert(account)).withSelfRel())); /// HERE IS MY TRY
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().build().toUri());
return new ResponseEntity<>(null, httpHeaders, HttpStatus.CREATED);
}
Thank you in advance!
you can not use method inside the same one
Try this :-
Link self=linkTo(AccountController.class).slash(account.getId()).withSelfRel();
Or you can refer from given link

Design issue in Spring app: in which layer (web, service or repository) should I retrieve the currently logged in user?

I work on a CRUD Spring app. Let me explain a basic use case:
A user can save an Advertisement. As of now I retrieve the currently logged in member in the web/controller layer and then pass it on to the service layer so that it can be set on the advertisement (the currently logged in user is the owner of the Advertisement; it is retrieve using Spring Security and my custom annotation: #CurrentMember).
In controller layer:
#RequestMapping(value = "/family/new", method = RequestMethod.POST, produces = "text/html")
public String newFamilyAdvertisement(
#ModelAttribute("advertisementInfo") #Validated(value = ValidationGroups.AdvertisementCreation.class) FamilyAdvertisementInfo familyAdvertisementInfo,
BindingResult bindingResult, Model model, #CurrentMember Member member) {
if (bindingResult.hasErrors()) {
populateFamilyAdvertisementModel(model, familyAdvertisementInfo, member);
return "advertisement/family/new";
}
advertisementService.createAdvertisement(member, familyAdvertisementInfo.getAdvertisement(), familyAdvertisementInfo.getAddressReference());
return "redirect:/advertisement/family/new";
}
In service layer:
#Override
public void createAdvertisement(Member member, Advertisement advertisement, String addressReference) {
if (member == null || advertisement == null || addressReference == null || addressReference.isEmpty()) {
throw new IllegalArgumentException("One argument is null or empty");
}
Address address = geolocationService.retrieveAddressFromReference(addressReference);
advertisement.setAddress(address);
advertisement.setMember(member);//SET CURRENTLY LOGGED IN USER
advertisement.setValidated(Boolean.FALSE);
advertisement.setActive(Boolean.TRUE);
advertisement.setCreationDate(utils.now());
saveAdvertisement(advertisement);
}
Still in service layer(Roo ITD):
public void AdvertisementServiceImpl.saveAdvertisement(Advertisement advertisement) {
advertisementRepository.save(advertisement);
}
Now the interrogation I have is:
Should I retrieve the current user/member as early as possible (here in the web layer) and then pass it on until it is needed (here in the service layer)? OR
Should I retrieve the current user/member only when I need it (here in the service layer)?
Thats a matter of design and choices you need to made, usually you dont need to bother controller to pass member to service. It doesnt need any knowledge about user. You can easily load it in service so you api is shorter/cleaner.
But, in a case your api is used from some external project - then api should show what objects are needed to make it work.
To sum up, in your case I would load it in service.

Losing Google Analytics tracking due to Spring Security login redirect

I have a mailing campaign where all links include google analytics tracking code such as:
http://example.com/account/somePage.html?utm_source=example&utm_medium=email&utm_campaign=reminder
The context /account/** is protected via Spring security and once the user clicks on the link on the email, he is re-directed to login BEFORE actually seeing somePage.html. This way the first page that is displayed is something like /login.do which does not have the analytics tracking code. Therefore google does not track my source, medium and campaign parameters.
Any ideas how to solve?
Based on http://support.google.com/analytics/answer/1009614?hl=en , I updated my LoginController that shows the login page to redirect to /login?GOOGLE_PARAMATERS:
private static final String ALREADY_REDIRECTED = "ALREADY_REDIRECTED";
....
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView loginView(HttpServletRequest request, HttpServletResponse response){
....
Boolean alreadyRedirected = (Boolean) request.getSession().getAttribute(ALREADY_REDIRECTED);
if (alreadyRedirected==null){
SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
if (savedRequest!=null){
String source[] = savedRequest.getParameterValues("utm_source");
if (source!=null && source.length>0){
// we need to redirect with login instead
String mediums[] = savedRequest.getParameterValues("utm_medium");
String medium = mediums.length==0 ? "" : mediums[0];
String campaigns[] = savedRequest.getParameterValues("utm_campaign");
String campaign = campaigns.length==0 ? "" : campaigns[0];
String redirect = "redirect:/login?utm_source=" + source[0] + "&utm_medium=" + medium + "&utm_campaign=" + campaign;
mav.setViewName(redirect);
// mark not to do twice
request.getSession().setAttribute(ALREADY_REDIRECTED, new Boolean(true));
return mav;
}
}
}
We have similar problem and have solved with the next solution.
We have a signup form via Ajax, and in the callback if everything is OK we auto-login the user and lost Google Analytics tracking code for Funnel visualization because of Spring Security session invalidation and set up a new cookie.
What we have done by JS just before auto-login call the new user this
_gaq.push(['_trackPageview', '/signupDone']);
https://gist.github.com/moskinson/5418938
signupDone is a fake url that does not exists.
This way GA receive a call of a new url is loaded and we can track the funnel!
http://packageprogrammer.wordpress.com/2013/04/19/seguimiento-con-google-analytics-a-traves-del-login-con-spring-security/

Spring 3 RESTful return on POST (create)

I am new to RESTful services and their implementation on Spring 3. I would like your opinion on the best practices for returning type when a client creates a new resource in my server.
#RequestMapping(method = RequestMethod.POST,
value = "/organisation",
headers = "content-type=application/xml")
#ResponseStatus(HttpStatus.CREATED)
public ??? createOrganisation(#RequestBody String xml)
{
StreamSource source = new StreamSource(new StringReader(xml));
Organisation organisation = (Organisation) castorMarshaller.unmarshal(source);
// save
return ???;
}
A simple choice would be javax.ws.rs.core.Response, found in the Java EE's own restful services package. It - simply - tells what the web server should answer to the HTTP request.
For instance:
if (organisation != null)
return Response.ok().build();
else
return Response.serverError().build();
Custom response headers and other exotic things like that are possible with that return type too, but I don't think that would match with "best practices".
uh, I missed that #ResponseStatus(HttpStatus.CREATED)... I guess my answer was not much of help.
Maybe this will help instead: How to return generated ID in RESTful POST?
I would go for a ResponseEntity<byte[]> and you would have take care of the marshalling of your response on your controller method. Notice that you are basically scrapping the V in MVC, there is a MarshallingView on Spring but from experience I consider the previous solution much more flexible and easier to understand.
It is a good idea to return the newly created entity(with the generated id) wrapped in ResponseEntity. You can also set the HttpStatus in ResponseEntity based on the result of the operation.
#RequestMapping(method = RequestMethod.POST,
value = "/organization",
headers = "content-type=application/xml")
public ResponseEntity<Organization> createOrganisation(#RequestBody String xml) {
StreamSource source = new StreamSource(new StringReader(xml));
Organization organisation = (Organization) castorMarshaller.unmarshal(source);
// save
return new ResponseEntity<Organization>(organization, HttpStatus.OK);
}

Resources