diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseController.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseController.java index 3fa4062ccc8ca8b0a325ee6562b0ef914f272194..18d6fccfadcd3c41ce39d4c3d4cb8a2944bedc5f 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseController.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseController.java @@ -1,5 +1,6 @@ package org.fuseri.modulelanguageschool.course; +import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; @@ -12,6 +13,7 @@ import org.fuseri.model.dto.course.CourseDto; import org.fuseri.model.dto.course.LanguageTypeDto; import org.fuseri.model.dto.course.ProficiencyLevelDto; import org.fuseri.modulelanguageschool.ModuleLanguageSchoolApplication; +import org.fuseri.modulelanguageschool.user.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -19,6 +21,7 @@ import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.NativeWebRequest; import java.util.List; @@ -44,7 +47,7 @@ public class CourseController { * @param dto the CourseCreateDto containing the course data * @return the newly created CourseDto */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Create a new course") @PostMapping @ApiResponses({ @@ -68,7 +71,7 @@ public class CourseController { @ApiResponse(code = 200, message = "Course found"), @ApiResponse(code = 404, message = "Course not found") }) - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) public ResponseEntity<CourseDto> find(@PathVariable Long id) { CourseDto courseDto = courseFacade.findById(id); return ResponseEntity.ok(courseDto); @@ -80,8 +83,7 @@ public class CourseController { * @param page the page number to retrieve * @return the Result containing the requested page of CourseDtos */ - - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) @ApiOperation(value = "Retrieve a paginated list of courses") @GetMapping("/findAll") @ApiResponses(value = { @@ -99,7 +101,7 @@ public class CourseController { * @param lang the language to find courses of * @return the Result containing the requested page of CourseDtos */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) @ApiOperation(value = "Retrieve a paginated list of courses of a given language") @GetMapping("/findAllByLang") @ApiResponses({ @@ -118,7 +120,7 @@ public class CourseController { * @param prof the proficiency of the language * @return the Result containing the requested page of CourseDtos */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) @ApiOperation(value = "Retrieve a paginated list of courses of a given language and proficiency") @GetMapping("/findAllByLangProf") @ApiResponses({ @@ -138,7 +140,7 @@ public class CourseController { * @param dto the CourseCreateDto containing the updated course data * @return the updated CourseDto */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Update an existing course") @PutMapping("/update/{id}") @ApiResponses({ @@ -162,7 +164,7 @@ public class CourseController { @ApiResponse(code = 204, message = "Course deleted successfully"), @ApiResponse(code = 404, message = "Course not found") }) - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1"})) public ResponseEntity<Void> delete(@PathVariable Long id) { courseFacade.delete(id); return ResponseEntity.noContent().build(); @@ -171,13 +173,13 @@ public class CourseController { /** * Adds student to the existing course * - * @param id id of course to update - * @param studentId UserDto for the student + * @param id id of course to update + * @param studentId id of the student * @return the CourseDto representing the updated course */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Add student to the existing course") - @PatchMapping("/enrol/{id}") + @PatchMapping("/enrolStudent/{id}") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully enrolled student in course"), @ApiResponse(code = 404, message = "Course not found") @@ -188,23 +190,59 @@ public class CourseController { } /** - * Removes student from the existing course + * Adds currently signed-in student to the existing course * * @param id id of lecture to update - * @param studentId UserDto for the student + * @param request http request received with user email * @return the CourseDto representing the updated course */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) + @ApiOperation(value = "Add me to the existing course") + @PatchMapping("/enrol/{id}") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Successfully enrolled student in course"), + @ApiResponse(code = 404, message = "Course not found") + }) + public ResponseEntity<CourseDto> enrol(@PathVariable Long id, NativeWebRequest request) throws JsonProcessingException { + String email = UserService.getEmailFromRequest(request); + return ResponseEntity.ok(courseFacade.enrol(id, email)); + } + + /** + * Removes student from the existing course + * + * @param id id of lecture to update + * @param studentId ID of the student + * @return the CourseDto representing the updated course + */ + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Remove student from the existing course") - @PatchMapping("/expel/{id}") + @PatchMapping("/expelStudent/{id}") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully expelled student from course"), @ApiResponse(code = 404, message = "Course not found") }) public ResponseEntity<CourseDto> expel(@PathVariable Long id, @RequestParam Long studentId) { + return ResponseEntity.ok(courseFacade.expel(id, studentId)); + } - CourseDto updatedCourse = courseFacade.expel(id, studentId); - return ResponseEntity.ok(updatedCourse); + /** + * Removes currently signed-in student from the existing course + * + * @param id id of lecture to update + * @param request http request received with user email + * @return the CourseDto representing the updated course + */ + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) + @ApiOperation(value = "Add me to the existing course") + @PatchMapping("/expel/{id}") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Successfully expelled student in course"), + @ApiResponse(code = 404, message = "Course not found") + }) + public ResponseEntity<CourseDto> expel(@PathVariable Long id, NativeWebRequest request) throws JsonProcessingException { + String email = UserService.getEmailFromRequest(request); + return ResponseEntity.ok(courseFacade.expel(id, email)); } } diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java index 7640ae2653bd40b331dcb48623eb90188c15777e..1e0070cebb0203ed3a7c954659e8dd1bb4b001d6 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java @@ -1,5 +1,6 @@ package org.fuseri.modulelanguageschool.course; +import jakarta.persistence.EntityNotFoundException; import org.fuseri.model.dto.course.CourseCreateDto; import org.fuseri.model.dto.course.CourseDto; import org.fuseri.model.dto.course.LanguageTypeDto; @@ -70,8 +71,20 @@ public class CourseFacade { return courseMapper.mapToDto(courseService.enrol(id, student)); } + public CourseDto enrol(Long id, String email) { + var student = userService.findUserByEmail(email) + .orElseThrow(() -> new EntityNotFoundException("User with " + email + " email not found.")); + return courseMapper.mapToDto(courseService.enrol(id, student)); + } + public CourseDto expel(Long id, Long studentId) { var student = userService.find(studentId); return courseMapper.mapToDto(courseService.expel(id, student)); } + + public CourseDto expel(Long id, String email) { + var student = userService.findUserByEmail(email) + .orElseThrow(() -> new EntityNotFoundException("User with " + email + " email not found.")); + return courseMapper.mapToDto(courseService.expel(id, student)); + } } diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureController.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureController.java index 6a62e2e0ab5e16fb1d0e61ad6539fd62913b26df..92d77571c096cb07babd721081e661e761eed38d 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureController.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureController.java @@ -1,5 +1,6 @@ package org.fuseri.modulelanguageschool.lecture; +import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; @@ -10,10 +11,12 @@ import jakarta.validation.Valid; import org.fuseri.model.dto.lecture.LectureCreateDto; import org.fuseri.model.dto.lecture.LectureDto; import org.fuseri.modulelanguageschool.ModuleLanguageSchoolApplication; +import org.fuseri.modulelanguageschool.user.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.NativeWebRequest; import java.util.List; @@ -40,6 +43,7 @@ public class LectureController { * @param lecture the LectureDto representing the lecture to be created * @return the LectureDto representing the newly created lecture */ + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Create a new lecture") @PostMapping @ApiResponses(value = { @@ -58,7 +62,7 @@ public class LectureController { * @return the LectureDto representing the found lecture */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) @ApiOperation(value = "Retrieve a lecture by its ID") @GetMapping("find/{courseId}") @ApiResponses(value = { @@ -76,7 +80,7 @@ public class LectureController { * @param courseId the course to retrieve lectures from * @return the list of LectureDtos */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) @ApiOperation(value = "Retrieve a list of lectures for the corresponding course") @GetMapping("/findByCourse") @ApiResponses(value = { @@ -93,7 +97,7 @@ public class LectureController { * @param lecture the CourseCreateDto representing the updated lecture * @return the LectureDto representing the updated lecture */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Update an existing lecture") @PutMapping("/update/{id}") @ApiResponses(value = { @@ -110,7 +114,7 @@ public class LectureController { * * @param id the ID of the lecture to delete */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1"})) @ApiOperation(value = "Delete a lecture by its ID") @DeleteMapping("/delete/{id}") @ApiResponses(value = { @@ -130,7 +134,7 @@ public class LectureController { * @param lecturerId UserDto for the course lecturer * @return the LectureDto representing the updated lecture */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Add lecturer to the existing lecture") @PatchMapping("/setLecturer/{id}") @ApiResponses(value = { @@ -149,18 +153,36 @@ public class LectureController { * @param studentId id for the course student * @return the LectureDto representing the updated lecture */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Add student to the existing lecture") @PatchMapping("/enrol/{id}") @ApiResponses(value = { @ApiResponse(code = 200, message = "The lecture has been successfully updated"), - @ApiResponse(code = 400, message = "The request body is invalid"), @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist") }) public ResponseEntity<LectureDto> enrol(@PathVariable Long id, @RequestParam Long studentId) { return ResponseEntity.ok(lectureFacade.enrol(id, studentId)); } + /** + * Adds student to the existing lecture resource + * + * @param id id of lecture to update + * @param request http request received with user email + * @return the LectureDto representing the updated lecture + */ + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) + @ApiOperation(value = "Add student to the existing lecture") + @PatchMapping("/enrol/{id}") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "The lecture has been successfully updated"), + @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist") + }) + public ResponseEntity<LectureDto> enrol(@PathVariable Long id, NativeWebRequest request) throws JsonProcessingException { + String email = UserService.getEmailFromRequest(request); + return ResponseEntity.ok(lectureFacade.enrol(id, email)); + } + /** * Removes student from the existing lecture resource * @@ -168,15 +190,33 @@ public class LectureController { * @param studentId id for the course student * @return the LectureDto representing the updated lecture */ - @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME,scopes = {"test_1","test_2"})) + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME, scopes = {"test_1", "test_2"})) @ApiOperation(value = "Remove student from the existing lecture") - @PatchMapping("/expel/{id}") + @PatchMapping("/expelStudent/{id}") @ApiResponses(value = { @ApiResponse(code = 200, message = "The lecture has been successfully updated"), - @ApiResponse(code = 400, message = "The request body is invalid"), @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist") }) public ResponseEntity<LectureDto> expel(@PathVariable Long id, @RequestParam Long studentId) { return ResponseEntity.ok(lectureFacade.expel(id, studentId)); } + + /** + * Removes student from the existing lecture resource + * + * @param id id of lecture to update + * @param request http request received with user email + * @return the LectureDto representing the updated lecture + */ + @Operation(security = @SecurityRequirement(name = ModuleLanguageSchoolApplication.SECURITY_SCHEME_NAME)) + @ApiOperation(value = "Remove student from the existing lecture") + @PatchMapping("/expel/{id}") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "The lecture has been successfully updated"), + @ApiResponse(code = 404, message = "The lecture with the specified ID does not exist") + }) + public ResponseEntity<LectureDto> expel(@PathVariable Long id, NativeWebRequest request) throws JsonProcessingException { + String email = UserService.getEmailFromRequest(request); + return ResponseEntity.ok(lectureFacade.expel(id, email)); + } } \ No newline at end of file diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java index dbd9359d81345db74f3ab809772665dc35ad6919..32fbab363ed0277f3e4ad0ea82a5d8afd95652ba 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java @@ -1,5 +1,6 @@ package org.fuseri.modulelanguageschool.lecture; +import jakarta.persistence.EntityNotFoundException; import org.fuseri.model.dto.course.LanguageTypeDto; import org.fuseri.model.dto.course.ProficiencyLevelDto; import org.fuseri.model.dto.lecture.LectureCreateDto; @@ -70,16 +71,28 @@ public class LectureFacade { return lectureMapper.mapToList(lectureService.findAll(Language.valueOf(lang.name()), ProficiencyLevel.valueOf(prof.name()))); } - public LectureDto enrol(Long id, long studentId) { + public LectureDto enrol(Long id, Long studentId) { var student = userService.find(studentId); return lectureMapper.mapToDto(lectureService.enrol(id, student)); } + public LectureDto enrol(Long id, String email) { + var student = userService.findUserByEmail(email) + .orElseThrow(() -> new EntityNotFoundException("User with " + email + " email not found.")); + return lectureMapper.mapToDto(lectureService.enrol(id, student)); + } + public LectureDto expel(Long id, Long studentId) { var student = userService.find(studentId); return lectureMapper.mapToDto(lectureService.expel(id, student)); } + public LectureDto expel(Long id, String email) { + var student = userService.findUserByEmail(email) + .orElseThrow(() -> new EntityNotFoundException("User with " + email + " email not found.")); + return lectureMapper.mapToDto(lectureService.expel(id, student)); + } + public LectureDto setLecturer(Long id, Long lecturerId) { var lecturer = userService.find(lecturerId); return lectureMapper.mapToDto(lectureService.setLecturer(id, lecturer)); diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserService.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserService.java index a01d981000e4c0aaea04267e30446495c9fa81e5..03a2297143d7346937f88fbe2ba914a5cff5226b 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserService.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserService.java @@ -1,6 +1,10 @@ package org.fuseri.modulelanguageschool.user; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.EntityNotFoundException; +import jakarta.servlet.http.HttpServletRequest; import lombok.Getter; import org.fuseri.modulelanguageschool.common.DomainService; import org.fuseri.modulelanguageschool.common.UserWithEmailAlreadyExists; @@ -10,7 +14,9 @@ import org.fuseri.modulelanguageschool.course.ProficiencyLevel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.NativeWebRequest; +import java.util.Base64; import java.util.List; import java.util.Optional; @@ -61,4 +67,24 @@ public class UserService extends DomainService<User> { throw new EntityNotFoundException("User '" + id + "' not found."); } } + + public static String getEmailFromRequest(NativeWebRequest request) throws JsonProcessingException { + // retrieve the HttpServletRequest object + HttpServletRequest servletRequest = (HttpServletRequest) request.getNativeRequest(); + + // extract the authorization header + String authorizationHeader = servletRequest.getHeader("Authorization"); + + // extract the bearer token + String accessToken = authorizationHeader.substring(7); + + // decode the JWT token payload + String[] jwtParts = accessToken.split("\\."); + String jwtPayload = new String(Base64.getDecoder().decode(jwtParts[1])); + + // parse the JSON payload to retrieve the email + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(jwtPayload); + return jsonNode.get("sub").asText(); + } }