From 7b8ae8f9e06ea1bd2016c524cfe5491b3993bcd5 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Fri, 24 Apr 2020 02:21:57 +0530 Subject: [PATCH 01/17] Updated jpa repository to h2 from derby, Added spring devtools --- derby.log | 13 ------------- pom.xml | 9 +++++++-- src/main/resources/application.yml | 9 ++++++++- 3 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 derby.log diff --git a/derby.log b/derby.log deleted file mode 100644 index 8958f11..0000000 --- a/derby.log +++ /dev/null @@ -1,13 +0,0 @@ ----------------------------------------------------------------- -Mon Apr 13 16:49:18 IST 2020: -Booting Derby version The Apache Software Foundation - Apache Derby - 10.14.2.0 - (1828579): instance a816c00e-0171-7343-d5dd-00000a619e30 -on database directory memory:C:\Users\Haripr\git\springboot-course-data-api\testdb with class loader sun.misc.Launcher$AppClassLoader@42a57993 -Loaded from file:/C:/Users/Haripr/.m2/repository/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar -java.vendor=Oracle Corporation -java.runtime.version=1.8.0_221-b11 -user.dir=C:\Users\Haripr\git\springboot-course-data-api -os.name=Windows 10 -os.arch=amd64 -os.version=10.0 -derby.system.home=null -Database Class Loader started - derby.database.classpath='' diff --git a/pom.xml b/pom.xml index 9be6a8b..93486bc 100644 --- a/pom.xml +++ b/pom.xml @@ -32,10 +32,15 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-devtools + runtime + - org.apache.derby - derby + com.h2database + h2 runtime diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 801a36d..6c62f7a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,7 +1,14 @@ #tomcat configurations server: port: 8082 - + +spring: + h2: + console: + enabled: true + jpa: + show-sql: true + #spring boot actuator configurations management: endpoint: From cda1a4c5872359c0669aa9ca1315778ce2a1b97a Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Fri, 24 Apr 2020 03:19:40 +0530 Subject: [PATCH 02/17] Added ResponseEntity --- .../java/boot/controller/course/Course.java | 4 +- .../controller/course/CourseController.java | 38 +++++--- .../controller/course/CourseRepository.java | 4 +- .../boot/controller/course/CourseService.java | 7 +- .../controller/topic/TopicController.java | 89 +++++++++++-------- .../boot/controller/topic/TopicService.java | 7 +- 6 files changed, 86 insertions(+), 63 deletions(-) diff --git a/src/main/java/in/hp/java/boot/controller/course/Course.java b/src/main/java/in/hp/java/boot/controller/course/Course.java index faef2fc..682aca9 100644 --- a/src/main/java/in/hp/java/boot/controller/course/Course.java +++ b/src/main/java/in/hp/java/boot/controller/course/Course.java @@ -1,12 +1,12 @@ package in.hp.java.boot.controller.course; +import in.hp.java.boot.controller.topic.Topic; + import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.ManyToOne; -import in.hp.java.boot.controller.topic.Topic; - @Entity public class Course { diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseController.java b/src/main/java/in/hp/java/boot/controller/course/CourseController.java index 956ca7a..680fe96 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseController.java @@ -1,18 +1,13 @@ package in.hp.java.boot.controller.course; -import java.util.List; - +import in.hp.java.boot.controller.topic.Topic; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import in.hp.java.boot.controller.topic.Topic; +import java.net.URI; +import java.util.List; /** * Added Root RequestMapping topics URI @@ -26,6 +21,8 @@ public class CourseController { @Autowired private CourseService courseService; + private static final String SLASHPATH = "/"; + @GetMapping(value = "/{topicId}/courses") public List getCourses(@PathVariable String topicId) { return courseService.getAllCourses(topicId); @@ -37,15 +34,30 @@ public Course getCourse(@PathVariable String id) { } @PostMapping(value = "/{topicId}/courses") - public void addCourse(@PathVariable String topicId, @RequestBody Course course) { + public ResponseEntity addCourse(@PathVariable String topicId, @RequestBody Course course) { course.setTopic(new Topic(topicId, "", "")); courseService.addCourse(course); + + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .path(SLASHPATH + course.getId()) + .buildAndExpand() + .toUri(); + + return ResponseEntity.created(uri).build(); } @PutMapping(value = "/{topicId}/courses/{id}") - public void updateCourse(@RequestBody Course course, @PathVariable String topicId, @PathVariable String id) { + public ResponseEntity updateCourse(@RequestBody Course course, @PathVariable String topicId, @PathVariable String id) { course.setTopic(new Topic(topicId, "", "")); courseService.updateCourse(course); + + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .buildAndExpand() + .toUri(); + + return ResponseEntity.created(uri).build(); } @DeleteMapping(value = "/{topicId}/courses/{id}") diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java b/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java index aa8e4a1..bbf2889 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java @@ -1,9 +1,9 @@ package in.hp.java.boot.controller.course; -import java.util.List; - import org.springframework.data.repository.CrudRepository; +import java.util.List; + public interface CourseRepository extends CrudRepository { /* diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseService.java b/src/main/java/in/hp/java/boot/controller/course/CourseService.java index fa74a13..fbe396a 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseService.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseService.java @@ -1,12 +1,11 @@ package in.hp.java.boot.controller.course; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + @Service public class CourseService { diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java index 94b55cd..40d9617 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java @@ -1,52 +1,65 @@ package in.hp.java.boot.controller.topic; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; +import java.util.List; /** * Added Root RequestMapping topics URI - * @author haripr * + * @author haripr */ @RestController @RequestMapping("/topics") public class TopicController { - @Autowired - private TopicService topicService; - - @GetMapping - public List getTopics() { - return topicService.getAllTopics(); - } - - @GetMapping(value = "/{id}") - public Topic getTopic(@PathVariable String id) { - return topicService.getTopic(id); - } - - @PostMapping - public void addTopic(@RequestBody Topic topic) { - topicService.addTopic(topic); - } - - @PutMapping(value = "/{id}") - public void updateTopic(@RequestBody Topic topic, @PathVariable String id) { - topicService.updateTopic(topic); - } - - @DeleteMapping(value = "/{id}") - public void deleteTopic(@PathVariable String id) { - topicService.deleteTopic(id); - } + @Autowired + private TopicService topicService; + + private static final String SLASHPATH = "/"; + + @GetMapping + public List getTopics() { + return topicService.getAllTopics(); + } + + @GetMapping(value = "/{id}") + public Topic getTopic(@PathVariable String id) { + return topicService.getTopic(id); + } + + @PostMapping + public ResponseEntity addTopic(@RequestBody Topic topic) { + topicService.addTopic(topic); + + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .path(SLASHPATH + topic.getId()) + .buildAndExpand() + .toUri(); + + return ResponseEntity.created(uri).build(); + } + + @PutMapping(value = "/{id}") + public ResponseEntity updateTopic(@RequestBody Topic topic, @PathVariable String id) { + topicService.updateTopic(topic); + + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .buildAndExpand() + .toUri(); + + return ResponseEntity.created(uri).build(); + } + + @DeleteMapping(value = "/{id}") + public void deleteTopic(@PathVariable String id) { + topicService.deleteTopic(id); + } } \ No newline at end of file diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java b/src/main/java/in/hp/java/boot/controller/topic/TopicService.java index cd38888..982157c 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicService.java @@ -1,12 +1,11 @@ package in.hp.java.boot.controller.topic; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + /** * @Service - Indicates that this is a singleton * @author haripr From 3af6785a0e98ccc28c3d25cbaa1a78e288864dfa Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Fri, 24 Apr 2020 03:46:26 +0530 Subject: [PATCH 03/17] Refactored from CrudRepository to JpaRepository --- .../hp/java/boot/controller/course/CourseController.java | 8 +------- .../hp/java/boot/controller/course/CourseRepository.java | 4 ++-- .../in/hp/java/boot/controller/topic/TopicController.java | 8 +------- .../in/hp/java/boot/controller/topic/TopicRepository.java | 4 ++-- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseController.java b/src/main/java/in/hp/java/boot/controller/course/CourseController.java index 680fe96..2f938e6 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseController.java @@ -51,13 +51,7 @@ public ResponseEntity addCourse(@PathVariable String topicId, @RequestBo public ResponseEntity updateCourse(@RequestBody Course course, @PathVariable String topicId, @PathVariable String id) { course.setTopic(new Topic(topicId, "", "")); courseService.updateCourse(course); - - URI uri = ServletUriComponentsBuilder - .fromCurrentRequest() - .buildAndExpand() - .toUri(); - - return ResponseEntity.created(uri).build(); + return ResponseEntity.accepted().body(course); } @DeleteMapping(value = "/{topicId}/courses/{id}") diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java b/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java index bbf2889..3edf646 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java @@ -1,10 +1,10 @@ package in.hp.java.boot.controller.course; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; -public interface CourseRepository extends CrudRepository { +public interface CourseRepository extends JpaRepository { /* * Custom method to retrieve list of courses by passing "name" as property diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java index 40d9617..0c5c1d7 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java @@ -48,13 +48,7 @@ public ResponseEntity addTopic(@RequestBody Topic topic) { @PutMapping(value = "/{id}") public ResponseEntity updateTopic(@RequestBody Topic topic, @PathVariable String id) { topicService.updateTopic(topic); - - URI uri = ServletUriComponentsBuilder - .fromCurrentRequest() - .buildAndExpand() - .toUri(); - - return ResponseEntity.created(uri).build(); + return ResponseEntity.accepted().body(topic); } @DeleteMapping(value = "/{id}") diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java b/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java index faae213..19acb72 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java @@ -1,5 +1,5 @@ package in.hp.java.boot.controller.topic; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.jpa.repository.JpaRepository; -public interface TopicRepository extends CrudRepository {} +public interface TopicRepository extends JpaRepository {} From c72b739f3f13a709d4caf8b99afce6d333e84dcf Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Fri, 24 Apr 2020 04:28:27 +0530 Subject: [PATCH 04/17] Added Basic Exception Handling for Not Found Resources --- .../controller/course/CourseController.java | 82 +++++++++---------- .../boot/controller/course/CourseService.java | 26 +++--- .../exceptions/ResourceNotFoundException.java | 11 +++ .../controller/topic/TopicController.java | 2 +- .../boot/controller/topic/TopicService.java | 27 +++--- src/main/resources/application.yml | 3 + 6 files changed, 78 insertions(+), 73 deletions(-) create mode 100644 src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseController.java b/src/main/java/in/hp/java/boot/controller/course/CourseController.java index 2f938e6..c2bd99d 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseController.java @@ -11,52 +11,52 @@ /** * Added Root RequestMapping topics URI - * @author haripr * + * @author haripr */ @RestController @RequestMapping("/topics") public class CourseController { - @Autowired - private CourseService courseService; - - private static final String SLASHPATH = "/"; - - @GetMapping(value = "/{topicId}/courses") - public List getCourses(@PathVariable String topicId) { - return courseService.getAllCourses(topicId); - } - - @GetMapping(value = "/{topicId}/courses/{id}") - public Course getCourse(@PathVariable String id) { - return courseService.getCourse(id); - } - - @PostMapping(value = "/{topicId}/courses") - public ResponseEntity addCourse(@PathVariable String topicId, @RequestBody Course course) { - course.setTopic(new Topic(topicId, "", "")); - courseService.addCourse(course); - - URI uri = ServletUriComponentsBuilder - .fromCurrentRequest() - .path(SLASHPATH + course.getId()) - .buildAndExpand() - .toUri(); - - return ResponseEntity.created(uri).build(); - } - - @PutMapping(value = "/{topicId}/courses/{id}") - public ResponseEntity updateCourse(@RequestBody Course course, @PathVariable String topicId, @PathVariable String id) { - course.setTopic(new Topic(topicId, "", "")); - courseService.updateCourse(course); - return ResponseEntity.accepted().body(course); - } - - @DeleteMapping(value = "/{topicId}/courses/{id}") - public void deleteCourse(@PathVariable String id) { - courseService.deleteCourse(id); - } + @Autowired + private CourseService courseService; + + private static final String SLASHPATH = "/"; + + @GetMapping(value = "/{topicId}/courses") + public List getCourses(@PathVariable String topicId) { + return courseService.getAllCourses(topicId); + } + + @GetMapping(value = "/{topicId}/courses/{id}") + public Course getCourse(@PathVariable String id) { + return courseService.getCourse(id); + } + + @PostMapping(value = "/{topicId}/courses") + public ResponseEntity addCourse(@PathVariable String topicId, @RequestBody Course course) { + course.setTopic(new Topic(topicId, "", "")); + courseService.addCourse(course); + + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .path(SLASHPATH + course.getId()) + .buildAndExpand() + .toUri(); + + return ResponseEntity.created(uri).build(); + } + + @PutMapping(value = "/{topicId}/courses/{id}") + public ResponseEntity updateCourse(@RequestBody Course course, @PathVariable String topicId, @PathVariable String id) { + course.setTopic(new Topic(topicId, "", "")); + courseService.updateCourse(course, id); + return ResponseEntity.accepted().body(course); + } + + @DeleteMapping(value = "/{topicId}/courses/{id}") + public void deleteCourse(@PathVariable String id) { + courseService.deleteCourse(id); + } } \ No newline at end of file diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseService.java b/src/main/java/in/hp/java/boot/controller/course/CourseService.java index fbe396a..155597d 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseService.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseService.java @@ -1,10 +1,12 @@ package in.hp.java.boot.controller.course; +import in.hp.java.boot.controller.exceptions.ResourceNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; @Service public class CourseService { @@ -16,8 +18,6 @@ public class CourseService { private CourseRepository courseRepository; public List getAllCourses(String topicId) { - List courses = new ArrayList<>(); - /* * The findAll() method of Spring JPA returns an Iterable of Topic objects after * retrieving it from the DB, all the connections and other stuffs are taken @@ -28,20 +28,14 @@ public List getAllCourses(String topicId) { * Spring Data JPA accepts a topic id and returns back all the courses * associated with the topic */ - courseRepository.findByTopicId(topicId).forEach(courses::add); - - return courses; + return courseRepository.findByTopicId(topicId).stream().collect(Collectors.toList()); } public Course getCourse(String id) { /** - * Ideally Optional.isPresent should be used - * Optional course = courseRepository.findById(id); - * return course.ifPresent() ? course.get() : null; - * or - * return course.orElse(null); + * Using orElseThrow to throw unchecked exception */ - return courseRepository.findById(id).orElse(null); + return courseRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(id)); } public void addCourse(Course course) { @@ -51,12 +45,14 @@ public void addCourse(Course course) { courseRepository.save(course); } - public void updateCourse(Course course) { - courseRepository.save(course); + public void updateCourse(Course course, String id) { + if (Objects.nonNull(getCourse(id))) + courseRepository.save(course); } public void deleteCourse(String id) { - courseRepository.deleteById(id); + if (Objects.nonNull(getCourse(id))) + courseRepository.deleteById(id); } } diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java b/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java new file mode 100644 index 0000000..59b62e2 --- /dev/null +++ b/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java @@ -0,0 +1,11 @@ +package in.hp.java.boot.controller.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException{ + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java index 0c5c1d7..451f45a 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java @@ -47,7 +47,7 @@ public ResponseEntity addTopic(@RequestBody Topic topic) { @PutMapping(value = "/{id}") public ResponseEntity updateTopic(@RequestBody Topic topic, @PathVariable String id) { - topicService.updateTopic(topic); + topicService.updateTopic(topic, id); return ResponseEntity.accepted().body(topic); } diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java b/src/main/java/in/hp/java/boot/controller/topic/TopicService.java index 982157c..512c0a7 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicService.java @@ -1,10 +1,12 @@ package in.hp.java.boot.controller.topic; +import in.hp.java.boot.controller.exceptions.ResourceNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** * @Service - Indicates that this is a singleton @@ -21,24 +23,15 @@ public class TopicService { private TopicRepository topicRepository; public List getAllTopics() { - List topics = new ArrayList<>(); - - /* - * The findAll() method of Spring JPA returns an Iterable of Topic objects after - * retrieving it from the DB, all the connections and other stuffs are taken - * care by Spring We need to iterate through the Iterable and create a list and - * send it back, for that we are using MethodReferences in Java 8. - */ - topicRepository.findAll().forEach(topics::add); - - return topics; + return topicRepository.findAll().stream().collect(Collectors.toList()); } public Topic getTopic(String id) { /** * orElse of Optional checks and return if the Optional has value or null + * orElseThrow will throw the supplied exception if Optional is null or empty */ - return topicRepository.findById(id).orElse(null); + return topicRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(id)); } public void addTopic(Topic topic) { @@ -48,16 +41,18 @@ public void addTopic(Topic topic) { topicRepository.save(topic); } - public void updateTopic(Topic topic) { + public void updateTopic(Topic topic, String id) { /* * Same method save() checks the primary key existence, and creates or updates * accordingly */ - topicRepository.save(topic); + if (Objects.nonNull(getTopic(id))) + topicRepository.save(topic); } public void deleteTopic(String id) { - topicRepository.deleteById(id); + if (Objects.nonNull(getTopic(id))) + topicRepository.deleteById(id); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6c62f7a..e70d5b2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,9 @@ #tomcat configurations server: port: 8082 + error: + # to exclude stack trace in custom exception messages + include-stacktrace: never spring: h2: From cea8e237f956ecd2b4b2ed26348fc9000f1a59dc Mon Sep 17 00:00:00 2001 From: Hari Date: Fri, 24 Apr 2020 06:07:54 +0530 Subject: [PATCH 05/17] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5b3874d..83d1a68 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ /nbdist/ /.nb-gradle/ /bin/ +/.idea/ From 4ec173569e08ed4c6694577f569c6593bc74e340 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Fri, 24 Apr 2020 23:57:27 +0530 Subject: [PATCH 06/17] Added custom exception handler as an advice for all controllers --- .../exceptions/CustomExceptionHandler.java | 59 +++++++++++++++++++ .../exceptions/GenericException.java | 25 ++++++++ .../exceptions/ResourceNotFoundException.java | 5 ++ 3 files changed, 89 insertions(+) create mode 100644 src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java create mode 100644 src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java b/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java new file mode 100644 index 0000000..5ec0d01 --- /dev/null +++ b/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java @@ -0,0 +1,59 @@ +package in.hp.java.boot.controller.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.util.Date; + +/** + * Custom Exception Handler + * Used to impose org level exception standard + * + * @RestController - this acts as a controller as it directly sends response + * @ControllerAdvice + * - the methods in the class should be common to all controllers, + * - hence aop kind of advice is used + * + * Any exceptions returned from any controller comes to this handler + * The method is executed based on @ExceptionHandler advice + */ +@RestController +@ControllerAdvice +public class CustomExceptionHandler extends ResponseEntityExceptionHandler { + + /** + * The below method is used to handle all the exceptions reported by controller + * It returns back custom exception with status code 500 + * + * request.getDescription(false) - here false is passed to not reveal client info + * + * @param ex + * @param request + * @return + */ + @ExceptionHandler(Exception.class) + public final ResponseEntity handleAllException(Exception ex, WebRequest request) { + GenericException genericException = new GenericException( + new Date().toString(), ex.getMessage(), request.getDescription(false)); + return new ResponseEntity(genericException, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * This method is executed for only ResourceNotFoundException + * + * @param ex + * @param request + * @return + */ + @ExceptionHandler(ResourceNotFoundException.class) + public final ResponseEntity handleAllException(ResourceNotFoundException ex, WebRequest request) { + GenericException genericException = new GenericException( + new Date().toString(), ex.getMessage(), request.getDescription(false)); + return new ResponseEntity(genericException, HttpStatus.NOT_FOUND); + } +} diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java b/src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java new file mode 100644 index 0000000..475880d --- /dev/null +++ b/src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java @@ -0,0 +1,25 @@ +package in.hp.java.boot.controller.exceptions; + +public class GenericException { + private String timestamp; + private String message; + private String details; + + public GenericException(String timestamp, String message, String details) { + this.timestamp = timestamp; + this.message = message; + this.details = details; + } + + public String getTimestamp() { + return timestamp; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } +} diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java b/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java index 59b62e2..31320ed 100644 --- a/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java +++ b/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java @@ -3,6 +3,11 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; +/** + * Custom exception + * @ResponseStatus(HttpStatus.NOT_FOUND) + * - throws spring format exception but with the given error code + */ @ResponseStatus(HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException{ public ResourceNotFoundException(String message) { From af71bd258e93c572197442970814c6520f11a910 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sat, 25 Apr 2020 00:35:53 +0530 Subject: [PATCH 07/17] Added Validations and Handling of Invalid Messages --- .../java/boot/controller/course/Course.java | 4 +++ .../controller/course/CourseController.java | 3 ++- .../exceptions/CustomExceptionHandler.java | 25 +++++++++++++++++-- .../hp/java/boot/controller/topic/Topic.java | 4 +++ .../controller/topic/TopicController.java | 8 +++++- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/main/java/in/hp/java/boot/controller/course/Course.java b/src/main/java/in/hp/java/boot/controller/course/Course.java index 682aca9..07fc7e5 100644 --- a/src/main/java/in/hp/java/boot/controller/course/Course.java +++ b/src/main/java/in/hp/java/boot/controller/course/Course.java @@ -6,12 +6,16 @@ import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.ManyToOne; +import javax.validation.constraints.Size; @Entity public class Course { @Id + @Size(min = 2, message = "Id should be of 2 chars minimum") private String id; + + @Size(min = 4, message = "Name should be of 4 chars minimum") private String name; private String description; diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseController.java b/src/main/java/in/hp/java/boot/controller/course/CourseController.java index c2bd99d..ce76677 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/controller/course/CourseController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import javax.validation.Valid; import java.net.URI; import java.util.List; @@ -34,7 +35,7 @@ public Course getCourse(@PathVariable String id) { } @PostMapping(value = "/{topicId}/courses") - public ResponseEntity addCourse(@PathVariable String topicId, @RequestBody Course course) { + public ResponseEntity addCourse(@PathVariable String topicId, @Valid @RequestBody Course course) { course.setTopic(new Topic(topicId, "", "")); courseService.addCourse(course); diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java b/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java index 5ec0d01..144ca6c 100644 --- a/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java +++ b/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java @@ -1,7 +1,9 @@ package in.hp.java.boot.controller.exceptions; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestController; @@ -29,7 +31,7 @@ public class CustomExceptionHandler extends ResponseEntityExceptionHandler { /** * The below method is used to handle all the exceptions reported by controller * It returns back custom exception with status code 500 - * + *

* request.getDescription(false) - here false is passed to not reveal client info * * @param ex @@ -56,4 +58,23 @@ public final ResponseEntity handleAllException(ResourceNotFoundException new Date().toString(), ex.getMessage(), request.getDescription(false)); return new ResponseEntity(genericException, HttpStatus.NOT_FOUND); } -} + + /** + * Overriding method to handle MethodArgumentNotValidException + * We can fetch the result of validation from "BindingResult" + * + * @param ex + * @param headers + * @param status + * @param request + * @return + */ + @Override + public ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + GenericException genericException = new GenericException( + new Date().toString(), "Validation failed for input.", ex.getBindingResult().toString() + ); + return new ResponseEntity<>(genericException, status); + } +} \ No newline at end of file diff --git a/src/main/java/in/hp/java/boot/controller/topic/Topic.java b/src/main/java/in/hp/java/boot/controller/topic/Topic.java index f898e25..e836cd7 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/Topic.java +++ b/src/main/java/in/hp/java/boot/controller/topic/Topic.java @@ -2,12 +2,16 @@ import javax.persistence.Entity; import javax.persistence.Id; +import javax.validation.constraints.Size; @Entity public class Topic { @Id + @Size(min = 2, message = "Id should be of 2 chars minimum") private String id; + + @Size(min = 4, message = "Name should be of 4 chars minimum") private String name; private String description; diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java index 451f45a..34470fb 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/controller/topic/TopicController.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import javax.validation.Valid; import java.net.URI; import java.util.List; @@ -32,8 +33,13 @@ public Topic getTopic(@PathVariable String id) { return topicService.getTopic(id); } + /** + * @Valid - used to validated the bean with the specified validators in bean class + * @param topic + * @return + */ @PostMapping - public ResponseEntity addTopic(@RequestBody Topic topic) { + public ResponseEntity addTopic(@Valid @RequestBody Topic topic) { topicService.addTopic(topic); URI uri = ServletUriComponentsBuilder From b86b676c127a8d58a91dcc91e33c08ee9151d0c4 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sat, 25 Apr 2020 01:53:47 +0530 Subject: [PATCH 08/17] Refactored Package --- .../java/in/hp/java/boot/{controller => }/course/Course.java | 4 ++-- .../java/boot/{controller => }/course/CourseController.java | 4 ++-- .../java/boot/{controller => }/course/CourseRepository.java | 2 +- .../hp/java/boot/{controller => }/course/CourseService.java | 4 ++-- .../{controller => }/exceptions/CustomExceptionHandler.java | 2 +- .../boot/{controller => }/exceptions/GenericException.java | 2 +- .../exceptions/ResourceNotFoundException.java | 2 +- .../java/in/hp/java/boot/{controller => }/topic/Topic.java | 2 +- .../hp/java/boot/{controller => }/topic/TopicController.java | 2 +- .../hp/java/boot/{controller => }/topic/TopicRepository.java | 2 +- .../in/hp/java/boot/{controller => }/topic/TopicService.java | 4 ++-- 11 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/in/hp/java/boot/{controller => }/course/Course.java (94%) rename src/main/java/in/hp/java/boot/{controller => }/course/CourseController.java (95%) rename src/main/java/in/hp/java/boot/{controller => }/course/CourseRepository.java (96%) rename src/main/java/in/hp/java/boot/{controller => }/course/CourseService.java (93%) rename src/main/java/in/hp/java/boot/{controller => }/exceptions/CustomExceptionHandler.java (98%) rename src/main/java/in/hp/java/boot/{controller => }/exceptions/GenericException.java (91%) rename src/main/java/in/hp/java/boot/{controller => }/exceptions/ResourceNotFoundException.java (90%) rename src/main/java/in/hp/java/boot/{controller => }/topic/Topic.java (95%) rename src/main/java/in/hp/java/boot/{controller => }/topic/TopicController.java (97%) rename src/main/java/in/hp/java/boot/{controller => }/topic/TopicRepository.java (76%) rename src/main/java/in/hp/java/boot/{controller => }/topic/TopicService.java (92%) diff --git a/src/main/java/in/hp/java/boot/controller/course/Course.java b/src/main/java/in/hp/java/boot/course/Course.java similarity index 94% rename from src/main/java/in/hp/java/boot/controller/course/Course.java rename to src/main/java/in/hp/java/boot/course/Course.java index 07fc7e5..aad728c 100644 --- a/src/main/java/in/hp/java/boot/controller/course/Course.java +++ b/src/main/java/in/hp/java/boot/course/Course.java @@ -1,6 +1,6 @@ -package in.hp.java.boot.controller.course; +package in.hp.java.boot.course; -import in.hp.java.boot.controller.topic.Topic; +import in.hp.java.boot.topic.Topic; import javax.persistence.Entity; import javax.persistence.FetchType; diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseController.java b/src/main/java/in/hp/java/boot/course/CourseController.java similarity index 95% rename from src/main/java/in/hp/java/boot/controller/course/CourseController.java rename to src/main/java/in/hp/java/boot/course/CourseController.java index ce76677..f651ea4 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/course/CourseController.java @@ -1,6 +1,6 @@ -package in.hp.java.boot.controller.course; +package in.hp.java.boot.course; -import in.hp.java.boot.controller.topic.Topic; +import in.hp.java.boot.topic.Topic; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java b/src/main/java/in/hp/java/boot/course/CourseRepository.java similarity index 96% rename from src/main/java/in/hp/java/boot/controller/course/CourseRepository.java rename to src/main/java/in/hp/java/boot/course/CourseRepository.java index 3edf646..bd5ebb2 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseRepository.java +++ b/src/main/java/in/hp/java/boot/course/CourseRepository.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.course; +package in.hp.java.boot.course; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/in/hp/java/boot/controller/course/CourseService.java b/src/main/java/in/hp/java/boot/course/CourseService.java similarity index 93% rename from src/main/java/in/hp/java/boot/controller/course/CourseService.java rename to src/main/java/in/hp/java/boot/course/CourseService.java index 155597d..98ffdc7 100644 --- a/src/main/java/in/hp/java/boot/controller/course/CourseService.java +++ b/src/main/java/in/hp/java/boot/course/CourseService.java @@ -1,6 +1,6 @@ -package in.hp.java.boot.controller.course; +package in.hp.java.boot.course; -import in.hp.java.boot.controller.exceptions.ResourceNotFoundException; +import in.hp.java.boot.exceptions.ResourceNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java b/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java similarity index 98% rename from src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java rename to src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java index 144ca6c..bc83541 100644 --- a/src/main/java/in/hp/java/boot/controller/exceptions/CustomExceptionHandler.java +++ b/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.exceptions; +package in.hp.java.boot.exceptions; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java b/src/main/java/in/hp/java/boot/exceptions/GenericException.java similarity index 91% rename from src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java rename to src/main/java/in/hp/java/boot/exceptions/GenericException.java index 475880d..260d013 100644 --- a/src/main/java/in/hp/java/boot/controller/exceptions/GenericException.java +++ b/src/main/java/in/hp/java/boot/exceptions/GenericException.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.exceptions; +package in.hp.java.boot.exceptions; public class GenericException { private String timestamp; diff --git a/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java b/src/main/java/in/hp/java/boot/exceptions/ResourceNotFoundException.java similarity index 90% rename from src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java rename to src/main/java/in/hp/java/boot/exceptions/ResourceNotFoundException.java index 31320ed..e7c6833 100644 --- a/src/main/java/in/hp/java/boot/controller/exceptions/ResourceNotFoundException.java +++ b/src/main/java/in/hp/java/boot/exceptions/ResourceNotFoundException.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.exceptions; +package in.hp.java.boot.exceptions; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/src/main/java/in/hp/java/boot/controller/topic/Topic.java b/src/main/java/in/hp/java/boot/topic/Topic.java similarity index 95% rename from src/main/java/in/hp/java/boot/controller/topic/Topic.java rename to src/main/java/in/hp/java/boot/topic/Topic.java index e836cd7..8a639d7 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/Topic.java +++ b/src/main/java/in/hp/java/boot/topic/Topic.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.topic; +package in.hp.java.boot.topic; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java b/src/main/java/in/hp/java/boot/topic/TopicController.java similarity index 97% rename from src/main/java/in/hp/java/boot/controller/topic/TopicController.java rename to src/main/java/in/hp/java/boot/topic/TopicController.java index 34470fb..735b1a7 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/topic/TopicController.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.topic; +package in.hp.java.boot.topic; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java b/src/main/java/in/hp/java/boot/topic/TopicRepository.java similarity index 76% rename from src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java rename to src/main/java/in/hp/java/boot/topic/TopicRepository.java index 19acb72..db66152 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicRepository.java +++ b/src/main/java/in/hp/java/boot/topic/TopicRepository.java @@ -1,4 +1,4 @@ -package in.hp.java.boot.controller.topic; +package in.hp.java.boot.topic; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java b/src/main/java/in/hp/java/boot/topic/TopicService.java similarity index 92% rename from src/main/java/in/hp/java/boot/controller/topic/TopicService.java rename to src/main/java/in/hp/java/boot/topic/TopicService.java index 512c0a7..b7bfe34 100644 --- a/src/main/java/in/hp/java/boot/controller/topic/TopicService.java +++ b/src/main/java/in/hp/java/boot/topic/TopicService.java @@ -1,6 +1,6 @@ -package in.hp.java.boot.controller.topic; +package in.hp.java.boot.topic; -import in.hp.java.boot.controller.exceptions.ResourceNotFoundException; +import in.hp.java.boot.exceptions.ResourceNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; From 3bb8425e3f343d915827083d4a315243294f817a Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sat, 25 Apr 2020 02:19:19 +0530 Subject: [PATCH 09/17] Internationalization basics --- .../in/hp/java/CourseDataApiApplication.java | 35 ++++++++++++++++++ .../java/boot/InternationalizationDemo.java | 37 +++++++++++++++++++ src/main/resources/messages.properties | 1 + src/main/resources/messages_fr.properties | 1 + 4 files changed, 74 insertions(+) create mode 100644 src/main/java/in/hp/java/boot/InternationalizationDemo.java create mode 100644 src/main/resources/messages.properties create mode 100644 src/main/resources/messages_fr.properties diff --git a/src/main/java/in/hp/java/CourseDataApiApplication.java b/src/main/java/in/hp/java/CourseDataApiApplication.java index 65fdf7c..8aebdda 100644 --- a/src/main/java/in/hp/java/CourseDataApiApplication.java +++ b/src/main/java/in/hp/java/CourseDataApiApplication.java @@ -2,6 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +import java.util.Locale; @SpringBootApplication public class CourseDataApiApplication { @@ -9,4 +15,33 @@ public class CourseDataApiApplication { public static void main(String[] args) { SpringApplication.run(CourseDataApiApplication.class, args); } + + /** + * In this bean we are configuring default locale and returning + * If a locale is not being sent in request this default will be used + * @return + */ + @Bean + public LocaleResolver buildLocale() { + SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); + sessionLocaleResolver.setDefaultLocale(Locale.US); + return sessionLocaleResolver; + } + + /** + * ResourceBundle is collection of resources files in resource folder + * ResourceBundleMessageSource is collection of message source + * Here we are using it to bundle i18n messages + * + * @return + */ + @Bean + public ResourceBundleMessageSource messageSource() { + ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); + /** + * Basename is the common name of the files inside the bundle + */ + resourceBundleMessageSource.setBasename("messages"); + return resourceBundleMessageSource; + } } diff --git a/src/main/java/in/hp/java/boot/InternationalizationDemo.java b/src/main/java/in/hp/java/boot/InternationalizationDemo.java new file mode 100644 index 0000000..1c86b96 --- /dev/null +++ b/src/main/java/in/hp/java/boot/InternationalizationDemo.java @@ -0,0 +1,37 @@ +package in.hp.java.boot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Locale; + +@RestController() +public class InternationalizationDemo { + + /** + * Autowiring message source to read the messages against passed input + */ + @Autowired + ResourceBundleMessageSource messageSource; + + @GetMapping("/greeting") + public String greet() { + return "Good Morning"; + } + + /** + * Injecting the locale from request from the header + * Accept-Language - where locale is passed + * required - false, since we already have default locale configured + * + * @param locale + * @return + */ + @GetMapping("/i18n") + public String internationalGreeting(@RequestHeader(value = "Accept-Language", required = false) Locale locale) { + return messageSource.getMessage("good.morning.message", null, locale); + } +} diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..82b33d6 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1 @@ +good.morning.message = Good Morning \ No newline at end of file diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties new file mode 100644 index 0000000..151f193 --- /dev/null +++ b/src/main/resources/messages_fr.properties @@ -0,0 +1 @@ +good.morning.message = Bonjour \ No newline at end of file From 1816e681518f4b4f0a612a091d221738c8f3ffa4 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sat, 25 Apr 2020 02:30:20 +0530 Subject: [PATCH 10/17] Internationalization - cleanup and used simpler ways to implement i18n --- .../in/hp/java/CourseDataApiApplication.java | 27 ++++++------------- .../java/boot/InternationalizationDemo.java | 8 +++--- src/main/resources/application.yml | 3 +++ 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/main/java/in/hp/java/CourseDataApiApplication.java b/src/main/java/in/hp/java/CourseDataApiApplication.java index 8aebdda..322aa32 100644 --- a/src/main/java/in/hp/java/CourseDataApiApplication.java +++ b/src/main/java/in/hp/java/CourseDataApiApplication.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import org.springframework.web.servlet.i18n.SessionLocaleResolver; import java.util.Locale; @@ -19,29 +20,17 @@ public static void main(String[] args) { /** * In this bean we are configuring default locale and returning * If a locale is not being sent in request this default will be used + * + * Since we are fetching from header here + * replacing SessionLocaleResolver to AcceptHeaderLocaleResolver + * * @return */ @Bean public LocaleResolver buildLocale() { - SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver(); - sessionLocaleResolver.setDefaultLocale(Locale.US); - return sessionLocaleResolver; + AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver(); + acceptHeaderLocaleResolver.setDefaultLocale(Locale.US); + return acceptHeaderLocaleResolver; } - /** - * ResourceBundle is collection of resources files in resource folder - * ResourceBundleMessageSource is collection of message source - * Here we are using it to bundle i18n messages - * - * @return - */ - @Bean - public ResourceBundleMessageSource messageSource() { - ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); - /** - * Basename is the common name of the files inside the bundle - */ - resourceBundleMessageSource.setBasename("messages"); - return resourceBundleMessageSource; - } } diff --git a/src/main/java/in/hp/java/boot/InternationalizationDemo.java b/src/main/java/in/hp/java/boot/InternationalizationDemo.java index 1c86b96..0161b3a 100644 --- a/src/main/java/in/hp/java/boot/InternationalizationDemo.java +++ b/src/main/java/in/hp/java/boot/InternationalizationDemo.java @@ -1,6 +1,7 @@ package in.hp.java.boot; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; @@ -27,11 +28,12 @@ public String greet() { * Accept-Language - where locale is passed * required - false, since we already have default locale configured * - * @param locale + * Instead of accepting from header everywhere, we can use LocaleContextHolder to retrieve the value + * * @return */ @GetMapping("/i18n") - public String internationalGreeting(@RequestHeader(value = "Accept-Language", required = false) Locale locale) { - return messageSource.getMessage("good.morning.message", null, locale); + public String internationalGreeting() { + return messageSource.getMessage("good.morning.message", null, LocaleContextHolder.getLocale()); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e70d5b2..da8e6d4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,6 +11,9 @@ spring: enabled: true jpa: show-sql: true + # configuring basename here instead of @Bean config for ResourceBundleMessageSource + messages: + basename: messages #spring boot actuator configurations management: From 181bf6611ffe55de61be5c577959aad7f1c5ee42 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sat, 25 Apr 2020 02:38:48 +0530 Subject: [PATCH 11/17] Code Cleanup --- README.md | 2 +- src/main/java/in/hp/java/CourseDataApiApplication.java | 4 +--- src/main/java/in/hp/java/boot/InternationalizationDemo.java | 3 --- src/main/java/in/hp/java/boot/course/CourseRepository.java | 6 +++--- src/main/java/in/hp/java/boot/course/CourseService.java | 6 +++--- .../in/hp/java/boot/exceptions/CustomExceptionHandler.java | 4 ++-- .../java/in/hp/java/boot/exceptions/GenericException.java | 6 +++--- src/main/java/in/hp/java/boot/topic/TopicService.java | 6 +++--- 8 files changed, 16 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c413aad..58d699b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # springboot-course-data-api -Simple POC using Spring Boot to create a Course API. +A simple POC using Spring Boot to create a Course API. ---------------------------------------------------- Spring Boot Dependencies Used: diff --git a/src/main/java/in/hp/java/CourseDataApiApplication.java b/src/main/java/in/hp/java/CourseDataApiApplication.java index 322aa32..6f56190 100644 --- a/src/main/java/in/hp/java/CourseDataApiApplication.java +++ b/src/main/java/in/hp/java/CourseDataApiApplication.java @@ -3,10 +3,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; -import org.springframework.web.servlet.i18n.SessionLocaleResolver; import java.util.Locale; @@ -24,7 +22,7 @@ public static void main(String[] args) { * Since we are fetching from header here * replacing SessionLocaleResolver to AcceptHeaderLocaleResolver * - * @return + * @return AcceptHeaderLocaleResolver */ @Bean public LocaleResolver buildLocale() { diff --git a/src/main/java/in/hp/java/boot/InternationalizationDemo.java b/src/main/java/in/hp/java/boot/InternationalizationDemo.java index 0161b3a..df21bdc 100644 --- a/src/main/java/in/hp/java/boot/InternationalizationDemo.java +++ b/src/main/java/in/hp/java/boot/InternationalizationDemo.java @@ -4,11 +4,8 @@ import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; -import java.util.Locale; - @RestController() public class InternationalizationDemo { diff --git a/src/main/java/in/hp/java/boot/course/CourseRepository.java b/src/main/java/in/hp/java/boot/course/CourseRepository.java index bd5ebb2..f51727c 100644 --- a/src/main/java/in/hp/java/boot/course/CourseRepository.java +++ b/src/main/java/in/hp/java/boot/course/CourseRepository.java @@ -18,9 +18,9 @@ public interface CourseRepository extends JpaRepository { * Note, no implementation is required, * as Spring Data JPA takes care of it if created a proper method name following the conventions. */ - public List findByName(String name); + List findByName(String name); - public List findByDescription(String description); + List findByDescription(String description); /* * To Retrieve the courses based on Topic because it has foreign key dependencies, @@ -31,5 +31,5 @@ public interface CourseRepository extends JpaRepository { * Model -> in this case it is "Topic" * Property -> primary key name of that model "Id" */ - public List findByTopicId(String topicId); + List findByTopicId(String topicId); } diff --git a/src/main/java/in/hp/java/boot/course/CourseService.java b/src/main/java/in/hp/java/boot/course/CourseService.java index 98ffdc7..0715beb 100644 --- a/src/main/java/in/hp/java/boot/course/CourseService.java +++ b/src/main/java/in/hp/java/boot/course/CourseService.java @@ -4,9 +4,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; @Service public class CourseService { @@ -28,11 +28,11 @@ public List getAllCourses(String topicId) { * Spring Data JPA accepts a topic id and returns back all the courses * associated with the topic */ - return courseRepository.findByTopicId(topicId).stream().collect(Collectors.toList()); + return new ArrayList<>(courseRepository.findByTopicId(topicId)); } public Course getCourse(String id) { - /** + /* * Using orElseThrow to throw unchecked exception */ return courseRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(id)); diff --git a/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java b/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java index bc83541..3a9edea 100644 --- a/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java +++ b/src/main/java/in/hp/java/boot/exceptions/CustomExceptionHandler.java @@ -42,7 +42,7 @@ public class CustomExceptionHandler extends ResponseEntityExceptionHandler { public final ResponseEntity handleAllException(Exception ex, WebRequest request) { GenericException genericException = new GenericException( new Date().toString(), ex.getMessage(), request.getDescription(false)); - return new ResponseEntity(genericException, HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(genericException, HttpStatus.INTERNAL_SERVER_ERROR); } /** @@ -56,7 +56,7 @@ public final ResponseEntity handleAllException(Exception ex, WebRequest public final ResponseEntity handleAllException(ResourceNotFoundException ex, WebRequest request) { GenericException genericException = new GenericException( new Date().toString(), ex.getMessage(), request.getDescription(false)); - return new ResponseEntity(genericException, HttpStatus.NOT_FOUND); + return new ResponseEntity<>(genericException, HttpStatus.NOT_FOUND); } /** diff --git a/src/main/java/in/hp/java/boot/exceptions/GenericException.java b/src/main/java/in/hp/java/boot/exceptions/GenericException.java index 260d013..0c3eebf 100644 --- a/src/main/java/in/hp/java/boot/exceptions/GenericException.java +++ b/src/main/java/in/hp/java/boot/exceptions/GenericException.java @@ -1,9 +1,9 @@ package in.hp.java.boot.exceptions; public class GenericException { - private String timestamp; - private String message; - private String details; + private final String timestamp; + private final String message; + private final String details; public GenericException(String timestamp, String message, String details) { this.timestamp = timestamp; diff --git a/src/main/java/in/hp/java/boot/topic/TopicService.java b/src/main/java/in/hp/java/boot/topic/TopicService.java index b7bfe34..928d615 100644 --- a/src/main/java/in/hp/java/boot/topic/TopicService.java +++ b/src/main/java/in/hp/java/boot/topic/TopicService.java @@ -4,9 +4,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; /** * @Service - Indicates that this is a singleton @@ -23,11 +23,11 @@ public class TopicService { private TopicRepository topicRepository; public List getAllTopics() { - return topicRepository.findAll().stream().collect(Collectors.toList()); + return new ArrayList<>(topicRepository.findAll()); } public Topic getTopic(String id) { - /** + /* * orElse of Optional checks and return if the Optional has value or null * orElseThrow will throw the supplied exception if Optional is null or empty */ From a8488e56ecb8a27f92db60bce871186b517e21eb Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sun, 26 Apr 2020 01:14:13 +0530 Subject: [PATCH 12/17] Configured Swagger --- pom.xml | 12 ++++++++++++ src/main/java/in/hp/java/SwaggerConfig.java | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/java/in/hp/java/SwaggerConfig.java diff --git a/pom.xml b/pom.xml index 93486bc..b125f39 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,18 @@ runtime + + io.springfox + springfox-swagger2 + 2.9.2 + + + + io.springfox + springfox-swagger-ui + 2.9.2 + + com.h2database h2 diff --git a/src/main/java/in/hp/java/SwaggerConfig.java b/src/main/java/in/hp/java/SwaggerConfig.java new file mode 100644 index 0000000..1abfaa0 --- /dev/null +++ b/src/main/java/in/hp/java/SwaggerConfig.java @@ -0,0 +1,15 @@ +package in.hp.java; + +import org.springframework.context.annotation.Configuration; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + public Docket configureDocket() { + return new Docket(DocumentationType.SWAGGER_2); + } +} From 9a282b868d60776df40ba2786d95d6ec1b6f940f Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sun, 26 Apr 2020 01:29:30 +0530 Subject: [PATCH 13/17] Enhanced Swagger Configs --- src/main/java/in/hp/java/SwaggerConfig.java | 30 ++++++++++++++++++- .../java/in/hp/java/boot/course/Course.java | 8 ++++- .../java/in/hp/java/boot/topic/Topic.java | 6 ++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/main/java/in/hp/java/SwaggerConfig.java b/src/main/java/in/hp/java/SwaggerConfig.java index 1abfaa0..42bfe24 100644 --- a/src/main/java/in/hp/java/SwaggerConfig.java +++ b/src/main/java/in/hp/java/SwaggerConfig.java @@ -1,15 +1,43 @@ package in.hp.java; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.service.VendorExtension; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; +import java.util.*; + @Configuration @EnableSwagger2 public class SwaggerConfig { + private static final Contact DEFAULT_CONTACT = new Contact( + "Hariprasath", + "https://www.github.com/hariprasath-r", + ""); + + private static final ApiInfo DEFAULT_API_INFO = new ApiInfo( + "Course Data API", + "Provides Creation and Maintaining Courses", + "1.0", + "urn:tos", + DEFAULT_CONTACT, + "Apache 2.0", + "http://www.apache.org/licenses/LICENSE-2.0", + new ArrayList()); + + private static final Set DEFAULT_PRODUCES_CONSUMES = + new HashSet<>(Arrays.asList("application/json", "application.xml")); + + @Bean public Docket configureDocket() { - return new Docket(DocumentationType.SWAGGER_2); + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(DEFAULT_API_INFO) + .produces(DEFAULT_PRODUCES_CONSUMES) + .consumes(DEFAULT_PRODUCES_CONSUMES); } } diff --git a/src/main/java/in/hp/java/boot/course/Course.java b/src/main/java/in/hp/java/boot/course/Course.java index aad728c..d9d3405 100644 --- a/src/main/java/in/hp/java/boot/course/Course.java +++ b/src/main/java/in/hp/java/boot/course/Course.java @@ -1,6 +1,8 @@ package in.hp.java.boot.course; import in.hp.java.boot.topic.Topic; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -8,21 +10,24 @@ import javax.persistence.ManyToOne; import javax.validation.constraints.Size; +@ApiModel("Course Details") @Entity public class Course { @Id @Size(min = 2, message = "Id should be of 2 chars minimum") + @ApiModelProperty(notes = "Id should be of 2 chars minimum") private String id; @Size(min = 4, message = "Name should be of 4 chars minimum") + @ApiModelProperty(notes = "Name should be of 4 chars minimum") private String name; private String description; /* * Adding JPA @ManyToOne annotation to let Spring JPA know it needs to establish * a Foreign Key relationship - * + * * FetchType can be made LAZY */ @ManyToOne(fetch = FetchType.EAGER) @@ -80,3 +85,4 @@ public void setTopic(Topic topic) { } } + diff --git a/src/main/java/in/hp/java/boot/topic/Topic.java b/src/main/java/in/hp/java/boot/topic/Topic.java index 8a639d7..18a8c17 100644 --- a/src/main/java/in/hp/java/boot/topic/Topic.java +++ b/src/main/java/in/hp/java/boot/topic/Topic.java @@ -1,17 +1,23 @@ package in.hp.java.boot.topic; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + import javax.persistence.Entity; import javax.persistence.Id; import javax.validation.constraints.Size; @Entity +@ApiModel("Topic Details") public class Topic { @Id @Size(min = 2, message = "Id should be of 2 chars minimum") + @ApiModelProperty(notes = "Id should be of 2 chars minimum") private String id; @Size(min = 4, message = "Name should be of 4 chars minimum") + @ApiModelProperty(notes = "Name should be of 4 chars minimum") private String name; private String description; From eccadc018fb359be833bfda0b0dbeb1ed76e1cf8 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Sun, 26 Apr 2020 22:42:39 +0530 Subject: [PATCH 14/17] Configured Swagger using Java Brains --- src/main/java/in/hp/java/SwaggerConfig.java | 19 ++++++++++++++++++- .../hp/java/boot/topic/TopicController.java | 11 +++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/in/hp/java/SwaggerConfig.java b/src/main/java/in/hp/java/SwaggerConfig.java index 42bfe24..56ef9e1 100644 --- a/src/main/java/in/hp/java/SwaggerConfig.java +++ b/src/main/java/in/hp/java/SwaggerConfig.java @@ -2,6 +2,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.service.VendorExtension; @@ -9,7 +10,10 @@ import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; @Configuration @EnableSwagger2 @@ -33,9 +37,22 @@ public class SwaggerConfig { private static final Set DEFAULT_PRODUCES_CONSUMES = new HashSet<>(Arrays.asList("application/json", "application.xml")); + /** + * Configuring and building Docket Object + * select() - allows to build the Docket with ApiSelectBuilder + * apis() - allows to configure the basePackage from which the models will be read + * paths() - allows to configure URI/resource path + * build() - returns a Docket object + * + * @return + */ @Bean public Docket configureDocket() { return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.basePackage("in.hp.java")) +// .paths(PathSelectors.ant("/*")) + .build() // building Docker Object .apiInfo(DEFAULT_API_INFO) .produces(DEFAULT_PRODUCES_CONSUMES) .consumes(DEFAULT_PRODUCES_CONSUMES); diff --git a/src/main/java/in/hp/java/boot/topic/TopicController.java b/src/main/java/in/hp/java/boot/topic/TopicController.java index 735b1a7..bcac9d1 100644 --- a/src/main/java/in/hp/java/boot/topic/TopicController.java +++ b/src/main/java/in/hp/java/boot/topic/TopicController.java @@ -1,5 +1,7 @@ package in.hp.java.boot.topic; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -24,21 +26,24 @@ public class TopicController { private static final String SLASHPATH = "/"; @GetMapping + @ApiOperation(value = "Return list of all Topics", response = Topic.class, responseContainer = "List") public List getTopics() { return topicService.getAllTopics(); } @GetMapping(value = "/{id}") + @ApiOperation(value = "Gets a Topic with specified Id", response = Topic.class) public Topic getTopic(@PathVariable String id) { return topicService.getTopic(id); } /** - * @Valid - used to validated the bean with the specified validators in bean class * @param topic * @return + * @Valid - used to validated the bean with the specified validators in bean class */ @PostMapping + @ApiOperation(value = "Adds a Topic") public ResponseEntity addTopic(@Valid @RequestBody Topic topic) { topicService.addTopic(topic); @@ -52,13 +57,15 @@ public ResponseEntity addTopic(@Valid @RequestBody Topic topic) { } @PutMapping(value = "/{id}") + @ApiOperation(value = "Updates the Topic") public ResponseEntity updateTopic(@RequestBody Topic topic, @PathVariable String id) { topicService.updateTopic(topic, id); return ResponseEntity.accepted().body(topic); } @DeleteMapping(value = "/{id}") - public void deleteTopic(@PathVariable String id) { + @ApiOperation(value = "Deletes a Topic") + public void deleteTopic(@ApiParam(required = true) @PathVariable String id) { topicService.deleteTopic(id); } From 8dda911aa4290ef9b93d1a5dc435b6372804491b Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Mon, 27 Apr 2020 09:25:57 +0530 Subject: [PATCH 15/17] Implemented Static Filtering for using @JsonIgnore --- src/main/java/in/hp/java/boot/course/Course.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/in/hp/java/boot/course/Course.java b/src/main/java/in/hp/java/boot/course/Course.java index d9d3405..e85b8e7 100644 --- a/src/main/java/in/hp/java/boot/course/Course.java +++ b/src/main/java/in/hp/java/boot/course/Course.java @@ -1,5 +1,7 @@ package in.hp.java.boot.course; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import in.hp.java.boot.topic.Topic; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -10,6 +12,11 @@ import javax.persistence.ManyToOne; import javax.validation.constraints.Size; +/** + * @JsonIgnoreProperties - used to ignore certain properties during Json conversion + * @JsonIgnore - recommended to use + */ +//@JsonIgnoreProperties(value = {"topic"}) @ApiModel("Course Details") @Entity public class Course { @@ -27,10 +34,12 @@ public class Course { /* * Adding JPA @ManyToOne annotation to let Spring JPA know it needs to establish * a Foreign Key relationship - * * FetchType can be made LAZY + * + * @JsonIgnore is used to ignore this field in response */ @ManyToOne(fetch = FetchType.EAGER) + @JsonIgnore private Topic topic; public Course() { From f271a77e86a28be0e908883ced1e27eb69b9c15a Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Mon, 27 Apr 2020 12:47:01 +0530 Subject: [PATCH 16/17] Added Dynamic Filtering Example Implementation --- src/main/java/in/hp/java/boot/course/Course.java | 3 ++- .../java/in/hp/java/boot/course/CourseController.java | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/in/hp/java/boot/course/Course.java b/src/main/java/in/hp/java/boot/course/Course.java index e85b8e7..40326d8 100644 --- a/src/main/java/in/hp/java/boot/course/Course.java +++ b/src/main/java/in/hp/java/boot/course/Course.java @@ -1,7 +1,6 @@ package in.hp.java.boot.course; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import in.hp.java.boot.topic.Topic; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -15,8 +14,10 @@ /** * @JsonIgnoreProperties - used to ignore certain properties during Json conversion * @JsonIgnore - recommended to use + * @JsonFilter - Indicated that the bean can be filtered */ //@JsonIgnoreProperties(value = {"topic"}) +//@JsonFilter("SomeBeanFilter") @ApiModel("Course Details") @Entity public class Course { diff --git a/src/main/java/in/hp/java/boot/course/CourseController.java b/src/main/java/in/hp/java/boot/course/CourseController.java index f651ea4..f31f6a6 100644 --- a/src/main/java/in/hp/java/boot/course/CourseController.java +++ b/src/main/java/in/hp/java/boot/course/CourseController.java @@ -26,6 +26,15 @@ public class CourseController { @GetMapping(value = "/{topicId}/courses") public List getCourses(@PathVariable String topicId) { + /* + * Code to add Dynamic Filter + SimpleBeanPropertyFilter simpleBeanPropertyFilter = + SimpleBeanPropertyFilter.filterOutAllExcept("topic"); + FilterProvider filterProvider = + new SimpleFilterProvider().addFilter("SomeBeanFilter", simpleBeanPropertyFilter); + MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(courseService.getAllCourses(topicId)); + mappingJacksonValue.setFilters(filterProvider); + */ return courseService.getAllCourses(topicId); } From 69c707169036d50014568c3d2a7630e395f706d3 Mon Sep 17 00:00:00 2001 From: Hariprasath Date: Mon, 27 Apr 2020 13:31:28 +0530 Subject: [PATCH 17/17] Added Basic Auth using Spring Security --- pom.xml | 4 ++++ src/main/resources/application.yml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index b125f39..61edf7e 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-actuator diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index da8e6d4..0f681c4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,6 +14,11 @@ spring: # configuring basename here instead of @Bean config for ResourceBundleMessageSource messages: basename: messages + # by default the username will be "user" and password will be generated and printed in console + security: + user: + name: username + password: password #spring boot actuator configurations management: