SlideShare a Scribd company logo
1 of 15
Download to read offline
Creating a Facebook Clone - Part XXV
The last piece of the server code is the WebService layer which is again relatively boilerplate driven. The WebServices mostly invoke the services layer without much
imagination. That's a good thing in engineering terms: boring is good. But it doesn't make a thrilling story. So please bare with me as this will pick up pace a bit when we
connect this to the client side!

We have 3 WebService classes: UserWebService, MediaWebService & PostWebService.
@Controller
@RequestMapping("/user")
@RestController
public class UserWebService {
@Autowired
private UserService users;
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
UserWebService
Just like the UserService this class is pretty big and also includes some additional capabilities from the NotificationService. Despite the fact that most of the class is
boilerplate there is still a lot of it so I'll try to keep it manageable by splitting it down a bit.

The WebService is located at the /user base which means any URL would be below that
@Controller
@RequestMapping("/user")
@RestController
public class UserWebService {
@Autowired
private UserService users;
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
UserWebService
We map 2 services in this single service. Since notifications has just one method it made sense bringing it here
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
getMessage(), 0);
}
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
UserWebService
The exception handlers grab one of the 3 exception types that can be thrown in one of the methods in this class and convert them to an error response object.

Notice that the error response is the ErrorDAO which generates JSON with an error message. This is the ErrorDAO class.
public class ErrorDAO {
private String error;
private int code;
public ErrorDAO() {
}
public ErrorDAO(String error, int code) {
this.error = error;
this.code = code;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
ErrorDAO
The Error DAO object is just a simple DAO that includes the error message and an optional error code.
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@RequestMapping(method=RequestMethod.POST, value="/login")
public @ResponseBody
UserDAO login(@RequestBody UserDAO u) throws LoginException {
return users.login(u.getEmail(), u.getPhone(), u.getPassword());
}
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
UserWebService
The user/login URL returns the response as JSON in the body and accept a JSON request in the body of the post. The exception will be converted to an ErrorDAO if it's
thrown
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@RequestMapping(method=RequestMethod.POST, value="/login")
public @ResponseBody
UserDAO login(@RequestBody UserDAO u) throws LoginException {
return users.login(u.getEmail(), u.getPhone(), u.getPassword());
}
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
UserWebService
The user/refresh method is a get operation notice that it doesn't accept an argument. It uses an HTTP header named auth for the authorization token
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
}
@RequestMapping(method=RequestMethod.GET, value="/verify")
public @ResponseBody
String verifyEmailOrPhone(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="code") String code,
@RequestParam(name="email") boolean email) {
if(users.verifyEmailOrPhone(auth, code, email)) {
return "OK";
}
return "ERROR";
}
UserWebService
Notice I use the auth header almost everywhere as it's a standard part of the request.

You will notice the code is very simple, it's declarative code that defines how the WebService will look before delegating to the underlying service class.
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
Next we have the operations that map an avatar. This means the URL `user/avata/userId` would return the avatar JPEG directly and would work even with the browser so
we can share this URL.
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
ResponseEntity lets us control the HTTP response including the mimetype and media content
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
In this case we send a 404 response when the user doesn't have an avatar
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
public String sendFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.sendFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
UserWebService
I use the GET method to make a change in the set-avatar request. This simplifies the code as it already requires a separate operation to upload the Media object
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
public String sendFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.sendFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
UserWebService
The friend request methods are trivial and nearly identical. They both accept the same arguments and pass them on to the underlying service calls.

I use a get operation because I don't want to add a DAO and get into the complexities of POST for this case.

Normally a get operation is a bad idea for an operation that triggers a change. I wouldn't do this on the web since it can lead to accidental resubmission of data.
However, since my client here is an app the use of a get operation simplifies some of the syntax.
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
public List<NotificationDAO> listNotifications(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="page") int page,
@RequestParam(name="size") int size) {
return notifications.listNotifications(auth, page, size);
}
@RequestMapping(method=RequestMethod.POST, value="/contacts")
public String uploadContacts(
@RequestHeader(name="auth", required=true) String auth,
@RequestBody List<ShadowUserDAO> contacts) {
users.uploadContacts(auth, contacts);
return "OK";
}
}
UserWebService
Finally the last piece of the UserWebService code is the notification & contacts support. This method allows us to page through the list of notifications fetching a new
page dynamically as needed
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
public List<NotificationDAO> listNotifications(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="page") int page,
@RequestParam(name="size") int size) {
return notifications.listNotifications(auth, page, size);
}
@RequestMapping(method=RequestMethod.POST, value="/contacts")
public String uploadContacts(
@RequestHeader(name="auth", required=true) String auth,
@RequestBody List<ShadowUserDAO> contacts) {
users.uploadContacts(auth, contacts);
return "OK";
}
}
UserWebService
We can submit a list of contacts for the shadow user through this API, notice that a List from JSON will implicitly translate to the given contact list. With that we finished
the relatively simple UserWebService.

More Related Content

Similar to Creating a Facebook Clone - Part XXV - Transcript.pdf

Jasigsakai12 columbia-customizes-cas
Jasigsakai12 columbia-customizes-casJasigsakai12 columbia-customizes-cas
Jasigsakai12 columbia-customizes-casellentuck
 
Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's NewTed Pennings
 
Creating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdfCreating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdfShaiAlmog1
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice PresentationDmitry Buzdin
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EEAlexis Hassler
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfShaiAlmog1
 
evoting ppt.pptx
evoting ppt.pptxevoting ppt.pptx
evoting ppt.pptxSurajBhan36
 
jQuery and Rails: Best Friends Forever
jQuery and Rails: Best Friends ForeverjQuery and Rails: Best Friends Forever
jQuery and Rails: Best Friends Foreverstephskardal
 
Building a Cloud API Server using Play(SCALA) & Riak
Building a Cloud API Server using  Play(SCALA) & Riak Building a Cloud API Server using  Play(SCALA) & Riak
Building a Cloud API Server using Play(SCALA) & Riak RajthilakMCA
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + SpringBryan Hsueh
 
ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CStutorialsruby
 
ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CStutorialsruby
 
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobileJavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobileLoiane Groner
 
May 2010 - RestEasy
May 2010 - RestEasyMay 2010 - RestEasy
May 2010 - RestEasyJBug Italy
 

Similar to Creating a Facebook Clone - Part XXV - Transcript.pdf (20)

Jasigsakai12 columbia-customizes-cas
Jasigsakai12 columbia-customizes-casJasigsakai12 columbia-customizes-cas
Jasigsakai12 columbia-customizes-cas
 
Spring 3: What's New
Spring 3: What's NewSpring 3: What's New
Spring 3: What's New
 
Codemotion appengine
Codemotion appengineCodemotion appengine
Codemotion appengine
 
Creating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdfCreating a Facebook Clone - Part XXVI - Transcript.pdf
Creating a Facebook Clone - Part XXVI - Transcript.pdf
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice Presentation
 
Testing in android
Testing in androidTesting in android
Testing in android
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
JPA 2.0
JPA 2.0JPA 2.0
JPA 2.0
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EE
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
evoting ppt.pptx
evoting ppt.pptxevoting ppt.pptx
evoting ppt.pptx
 
jQuery and Rails: Best Friends Forever
jQuery and Rails: Best Friends ForeverjQuery and Rails: Best Friends Forever
jQuery and Rails: Best Friends Forever
 
Building a Cloud API Server using Play(SCALA) & Riak
Building a Cloud API Server using  Play(SCALA) & Riak Building a Cloud API Server using  Play(SCALA) & Riak
Building a Cloud API Server using Play(SCALA) & Riak
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + Spring
 
React native by example by Vadim Ruban
React native by example by Vadim RubanReact native by example by Vadim Ruban
React native by example by Vadim Ruban
 
ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CS
 
ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CSASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CS
 
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobileJavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
 
May 2010 - RestEasy
May 2010 - RestEasyMay 2010 - RestEasy
May 2010 - RestEasy
 

More from ShaiAlmog1

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...ShaiAlmog1
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfShaiAlmog1
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfShaiAlmog1
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfShaiAlmog1
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfShaiAlmog1
 

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdf
 

Recently uploaded

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 

Recently uploaded (20)

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort ServiceHot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 

Creating a Facebook Clone - Part XXV - Transcript.pdf

  • 1. Creating a Facebook Clone - Part XXV The last piece of the server code is the WebService layer which is again relatively boilerplate driven. The WebServices mostly invoke the services layer without much imagination. That's a good thing in engineering terms: boring is good. But it doesn't make a thrilling story. So please bare with me as this will pick up pace a bit when we connect this to the client side! We have 3 WebService classes: UserWebService, MediaWebService & PostWebService.
  • 2. @Controller @RequestMapping("/user") @RestController public class UserWebService { @Autowired private UserService users; @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. UserWebService Just like the UserService this class is pretty big and also includes some additional capabilities from the NotificationService. Despite the fact that most of the class is boilerplate there is still a lot of it so I'll try to keep it manageable by splitting it down a bit. The WebService is located at the /user base which means any URL would be below that
  • 3. @Controller @RequestMapping("/user") @RestController public class UserWebService { @Autowired private UserService users; @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. UserWebService We map 2 services in this single service. Since notifications has just one method it made sense bringing it here
  • 4. @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. getMessage(), 0); } @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } UserWebService The exception handlers grab one of the 3 exception types that can be thrown in one of the methods in this class and convert them to an error response object. Notice that the error response is the ErrorDAO which generates JSON with an error message. This is the ErrorDAO class.
  • 5. public class ErrorDAO { private String error; private int code; public ErrorDAO() { } public ErrorDAO(String error, int code) { this.error = error; this.code = code; } public String getError() { return error; } public void setError(String error) { this.error = error; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } } ErrorDAO The Error DAO object is just a simple DAO that includes the error message and an optional error code.
  • 6. @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } @RequestMapping(method=RequestMethod.POST, value="/login") public @ResponseBody UserDAO login(@RequestBody UserDAO u) throws LoginException { return users.login(u.getEmail(), u.getPhone(), u.getPassword()); } @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); UserWebService The user/login URL returns the response as JSON in the body and accept a JSON request in the body of the post. The exception will be converted to an ErrorDAO if it's thrown
  • 7. @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } @RequestMapping(method=RequestMethod.POST, value="/login") public @ResponseBody UserDAO login(@RequestBody UserDAO u) throws LoginException { return users.login(u.getEmail(), u.getPhone(), u.getPassword()); } @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); UserWebService The user/refresh method is a get operation notice that it doesn't accept an argument. It uses an HTTP header named auth for the authorization token
  • 8. @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); } @RequestMapping(method=RequestMethod.GET, value="/verify") public @ResponseBody String verifyEmailOrPhone( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="code") String code, @RequestParam(name="email") boolean email) { if(users.verifyEmailOrPhone(auth, code, email)) { return "OK"; } return "ERROR"; } UserWebService Notice I use the auth header almost everywhere as it's a standard part of the request. You will notice the code is very simple, it's declarative code that defines how the WebService will look before delegating to the underlying service class.
  • 9. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService Next we have the operations that map an avatar. This means the URL `user/avata/userId` would return the avatar JPEG directly and would work even with the browser so we can share this URL.
  • 10. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService ResponseEntity lets us control the HTTP response including the mimetype and media content
  • 11. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService In this case we send a 404 response when the user doesn't have an avatar
  • 12. byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") public String sendFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.sendFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value UserWebService I use the GET method to make a change in the set-avatar request. This simplifies the code as it already requires a separate operation to upload the Media object
  • 13. @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") public String sendFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.sendFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") UserWebService The friend request methods are trivial and nearly identical. They both accept the same arguments and pass them on to the underlying service calls. I use a get operation because I don't want to add a DAO and get into the complexities of POST for this case. Normally a get operation is a bad idea for an operation that triggers a change. I wouldn't do this on the web since it can lead to accidental resubmission of data. However, since my client here is an app the use of a get operation simplifies some of the syntax.
  • 14. @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") public List<NotificationDAO> listNotifications( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="page") int page, @RequestParam(name="size") int size) { return notifications.listNotifications(auth, page, size); } @RequestMapping(method=RequestMethod.POST, value="/contacts") public String uploadContacts( @RequestHeader(name="auth", required=true) String auth, @RequestBody List<ShadowUserDAO> contacts) { users.uploadContacts(auth, contacts); return "OK"; } } UserWebService Finally the last piece of the UserWebService code is the notification & contacts support. This method allows us to page through the list of notifications fetching a new page dynamically as needed
  • 15. @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") public List<NotificationDAO> listNotifications( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="page") int page, @RequestParam(name="size") int size) { return notifications.listNotifications(auth, page, size); } @RequestMapping(method=RequestMethod.POST, value="/contacts") public String uploadContacts( @RequestHeader(name="auth", required=true) String auth, @RequestBody List<ShadowUserDAO> contacts) { users.uploadContacts(auth, contacts); return "OK"; } } UserWebService We can submit a list of contacts for the shadow user through this API, notice that a List from JSON will implicitly translate to the given contact list. With that we finished the relatively simple UserWebService.