From 82f596b9f3a5cf0ac4abdd02c9e99b64b8b16e4b Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Mon, 12 Mar 2018 15:20:04 +0100
Subject: [PATCH] Set Id and Revision HTTP headers for POST, PUT and PATCH
 responses

---
 .../controller/AbstractEntityController.java  | 27 ++++++++++++++++---
 .../arsnova/controller/AnswerController.java  |  9 ++++++-
 .../arsnova/controller/CommentController.java |  9 ++++++-
 .../arsnova/controller/ContentController.java |  8 +++++-
 .../arsnova/controller/MotdController.java    |  9 ++++++-
 .../arsnova/controller/RoomController.java    |  9 ++++++-
 .../arsnova/controller/UserController.java    |  9 ++++++-
 7 files changed, 70 insertions(+), 10 deletions(-)

diff --git a/src/main/java/de/thm/arsnova/controller/AbstractEntityController.java b/src/main/java/de/thm/arsnova/controller/AbstractEntityController.java
index ed7c1d38c..7876d0163 100644
--- a/src/main/java/de/thm/arsnova/controller/AbstractEntityController.java
+++ b/src/main/java/de/thm/arsnova/controller/AbstractEntityController.java
@@ -24,6 +24,8 @@ import de.thm.arsnova.services.FindQueryService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PatchMapping;
@@ -32,8 +34,11 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.util.UriComponentsBuilder;
 
 import javax.naming.OperationNotSupportedException;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Map;
@@ -47,6 +52,8 @@ import java.util.Set;
  */
 public abstract class AbstractEntityController<E extends Entity> {
 	private static final Logger logger = LoggerFactory.getLogger(AbstractEntityController.class);
+	protected static final String ENTITY_ID_HEADER = "Arsnova-Entity-Id";
+	protected static final String ENTITY_REVISION_HEADER = "Arsnova-Entity-Revision";
 	protected static final String DEFAULT_ROOT_MAPPING = "/";
 	protected static final String DEFAULT_ID_MAPPING = "/{id}";
 	protected static final String DEFAULT_FIND_MAPPING = "/find";
@@ -64,6 +71,8 @@ public abstract class AbstractEntityController<E extends Entity> {
 		this.entityService = entityService;
 	}
 
+	protected abstract String getMapping();
+
 	@GetMapping(GET_MAPPING)
 	public E get(@PathVariable final String id) {
 		return entityService.get(id);
@@ -75,21 +84,31 @@ public abstract class AbstractEntityController<E extends Entity> {
 	}
 
 	@PutMapping(PUT_MAPPING)
-	public void put(@RequestBody final E entity) {
+	public void put(@RequestBody final E entity, final HttpServletResponse httpServletResponse) {
 		E oldEntity = entityService.get(entity.getId());
 		entityService.update(oldEntity, entity);
+		httpServletResponse.setHeader(ENTITY_ID_HEADER, entity.getId());
+		httpServletResponse.setHeader(ENTITY_REVISION_HEADER, entity.getRevision());
 	}
 
 	@PostMapping(POST_MAPPING)
-	public void post(@RequestBody final E entity) {
+	@ResponseStatus(HttpStatus.CREATED)
+	public void post(@RequestBody final E entity, final HttpServletResponse httpServletResponse) {
 		entityService.create(entity);
+		final String uri = UriComponentsBuilder.fromPath(getMapping()).path(GET_MAPPING)
+				.buildAndExpand(entity.getId()).toUriString();
+		httpServletResponse.setHeader(HttpHeaders.LOCATION, uri);
+		httpServletResponse.setHeader(ENTITY_ID_HEADER, entity.getId());
+		httpServletResponse.setHeader(ENTITY_REVISION_HEADER, entity.getRevision());
 	}
 
 	@PatchMapping(PATCH_MAPPING)
-	public void patch(@PathVariable final String id, @RequestBody final Map<String, Object> changes)
-			throws IOException {
+	public void patch(@PathVariable final String id, @RequestBody final Map<String, Object> changes,
+					  final HttpServletResponse httpServletResponse) throws IOException {
 		E entity = entityService.get(id);
 		entityService.patch(entity, changes);
+		httpServletResponse.setHeader(ENTITY_ID_HEADER, entity.getId());
+		httpServletResponse.setHeader(ENTITY_REVISION_HEADER, entity.getRevision());
 	}
 
 	@DeleteMapping(DELETE_MAPPING)
diff --git a/src/main/java/de/thm/arsnova/controller/AnswerController.java b/src/main/java/de/thm/arsnova/controller/AnswerController.java
index 8eeb8e736..d0fd8606c 100644
--- a/src/main/java/de/thm/arsnova/controller/AnswerController.java
+++ b/src/main/java/de/thm/arsnova/controller/AnswerController.java
@@ -23,12 +23,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/answer")
+@RequestMapping(AnswerController.REQUEST_MAPPING)
 public class AnswerController extends AbstractEntityController<Answer> {
+	protected static final String REQUEST_MAPPING = "/answer";
+
 	private AnswerService answerService;
 
 	public AnswerController(final AnswerService answerService) {
 		super(answerService);
 		this.answerService = answerService;
 	}
+
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/CommentController.java b/src/main/java/de/thm/arsnova/controller/CommentController.java
index cfcb1579c..ea66759d7 100644
--- a/src/main/java/de/thm/arsnova/controller/CommentController.java
+++ b/src/main/java/de/thm/arsnova/controller/CommentController.java
@@ -23,12 +23,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/comment")
+@RequestMapping(CommentController.REQUEST_MAPPING)
 public class CommentController extends AbstractEntityController<Comment> {
+	protected static final String REQUEST_MAPPING = "/comment";
+
 	private CommentService commentService;
 
 	public CommentController(final CommentService commentService) {
 		super(commentService);
 		this.commentService = commentService;
 	}
+
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/ContentController.java b/src/main/java/de/thm/arsnova/controller/ContentController.java
index 1ba72f9b9..3856ed20a 100644
--- a/src/main/java/de/thm/arsnova/controller/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/ContentController.java
@@ -27,8 +27,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/content")
+@RequestMapping(ContentController.REQUEST_MAPPING)
 public class ContentController extends AbstractEntityController<Content> {
+	protected static final String REQUEST_MAPPING = "/content";
 	private static final String GET_ANSWER_STATISTICS_MAPPING = DEFAULT_ID_MAPPING + "/stats";
 
 	private ContentService contentService;
@@ -40,6 +41,11 @@ public class ContentController extends AbstractEntityController<Content> {
 		this.answerService = answerService;
 	}
 
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
+
 	@GetMapping(GET_ANSWER_STATISTICS_MAPPING)
 	public AnswerStatistics getAnswerStatistics(@PathVariable final String id) {
 		return answerService.getAllStatistics(id);
diff --git a/src/main/java/de/thm/arsnova/controller/MotdController.java b/src/main/java/de/thm/arsnova/controller/MotdController.java
index 1a3df379a..7193fc3b0 100644
--- a/src/main/java/de/thm/arsnova/controller/MotdController.java
+++ b/src/main/java/de/thm/arsnova/controller/MotdController.java
@@ -23,12 +23,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/motd")
+@RequestMapping(MotdController.REQUEST_MAPPING)
 public class MotdController extends AbstractEntityController<Motd> {
+	protected static final String REQUEST_MAPPING = "/motd";
+
 	private MotdService motdService;
 
 	public MotdController(final MotdService motdService) {
 		super(motdService);
 		this.motdService = motdService;
 	}
+
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/RoomController.java b/src/main/java/de/thm/arsnova/controller/RoomController.java
index 5f0797fbf..21fe4a851 100644
--- a/src/main/java/de/thm/arsnova/controller/RoomController.java
+++ b/src/main/java/de/thm/arsnova/controller/RoomController.java
@@ -23,12 +23,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/room")
+@RequestMapping(RoomController.REQUEST_MAPPING)
 public class RoomController extends AbstractEntityController<Room> {
+	protected static final String REQUEST_MAPPING = "/room";
+
 	private RoomService roomService;
 
 	public RoomController(final RoomService roomService) {
 		super(roomService);
 		this.roomService = roomService;
 	}
+
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/UserController.java b/src/main/java/de/thm/arsnova/controller/UserController.java
index b68b60a04..de5a94673 100644
--- a/src/main/java/de/thm/arsnova/controller/UserController.java
+++ b/src/main/java/de/thm/arsnova/controller/UserController.java
@@ -9,8 +9,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@RequestMapping("/user")
+@RequestMapping(UserController.REQUEST_MAPPING)
 public class UserController extends AbstractEntityController<UserProfile> {
+	protected static final String REQUEST_MAPPING = "/user";
+
 	private UserService userService;
 
 	public UserController(final UserService userService) {
@@ -18,6 +20,11 @@ public class UserController extends AbstractEntityController<UserProfile> {
 		this.userService = userService;
 	}
 
+	@Override
+	protected String getMapping() {
+		return REQUEST_MAPPING;
+	}
+
 	@PostMapping("/register")
 	public void register(@RequestBody LoginCredentials loginCredentials) {
 		userService.create(loginCredentials.getLoginId(), loginCredentials.getPassword());
-- 
GitLab