From 95101645e7ef0fe108753d6c23a7f8325913fbb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <xpokorn8@fi.muni.cz> Date: Wed, 5 Apr 2023 21:18:55 +0200 Subject: [PATCH] Course CRUD implementation, extensive change of id from String to Long --- .../model/dto/certificate/CertificateDto.java | 2 +- .../model/dto/common/DomainObjectDto.java | 2 +- .../model/dto/course/CourseCreateDto.java | 4 +- .../fuseri/model/dto/course/CourseDto.java | 16 +-- .../fuseri/model/dto/lecture/LectureDto.java | 2 +- .../org/fuseri/model/dto/user/UserDto.java | 5 +- application/module-language-school/pom.xml | 4 + .../common/DomainObject.java | 7 +- .../common/DomainService.java | 2 +- .../common/ResourceNotFoundException.java | 22 ++++ .../modulelanguageschool/course/Course.java | 5 +- .../course/CourseController.java | 37 +++--- .../course/CourseFacade.java | 49 ++++++++ .../course/CourseMapper.java | 27 +++++ .../course/CourseRepository.java | 9 ++ .../course/CourseService.java | 57 ++++++++++ .../user/UserController.java | 18 +-- .../user/UserRepository.java | 2 +- .../user/UserService.java | 2 +- .../src/main/resources/application.properties | 14 ++- .../course/CourseTest.java | 105 +++++++++--------- .../user/UserControllerTest.java | 14 +-- .../resources/application-test.properties | 14 +++ .../src/test/resources/logback.xml | 16 +++ 24 files changed, 330 insertions(+), 105 deletions(-) create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/ResourceNotFoundException.java create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseMapper.java create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseRepository.java create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseService.java create mode 100644 application/module-language-school/src/test/resources/application-test.properties create mode 100644 application/module-language-school/src/test/resources/logback.xml diff --git a/application/model/src/main/java/org/fuseri/model/dto/certificate/CertificateDto.java b/application/model/src/main/java/org/fuseri/model/dto/certificate/CertificateDto.java index 52597af3..6d55d577 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/certificate/CertificateDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/certificate/CertificateDto.java @@ -33,6 +33,6 @@ public class CertificateDto extends DomainObjectDto { private CertificateFileDto certificateFile; public CertificateDto() { - setId("0"); + setId(0L); } } diff --git a/application/model/src/main/java/org/fuseri/model/dto/common/DomainObjectDto.java b/application/model/src/main/java/org/fuseri/model/dto/common/DomainObjectDto.java index 2c1aff83..36ec98b0 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/common/DomainObjectDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/common/DomainObjectDto.java @@ -11,5 +11,5 @@ public abstract class DomainObjectDto { @NotBlank @NotNull - private String id; + private Long id; } diff --git a/application/model/src/main/java/org/fuseri/model/dto/course/CourseCreateDto.java b/application/model/src/main/java/org/fuseri/model/dto/course/CourseCreateDto.java index 88995655..64c78eec 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/course/CourseCreateDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/course/CourseCreateDto.java @@ -28,10 +28,10 @@ public class CourseCreateDto { @NotNull(message = "Language type is required") @Valid - private LanguageTypeDto languageTypeDto; + private LanguageTypeDto language; @NotNull(message = "Proficiency level is required") @Valid - private ProficiencyLevelDto proficiencyLevelDto; + private ProficiencyLevelDto proficiency; } diff --git a/application/model/src/main/java/org/fuseri/model/dto/course/CourseDto.java b/application/model/src/main/java/org/fuseri/model/dto/course/CourseDto.java index 544d06f5..18e5695b 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/course/CourseDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/course/CourseDto.java @@ -20,7 +20,7 @@ import java.util.List; */ @Getter @Setter -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) public class CourseDto extends DomainObjectDto { @NotBlank(message = "Course name is required") @@ -33,22 +33,22 @@ public class CourseDto extends DomainObjectDto { @NotNull(message = "Language type is required") @Valid - private LanguageTypeDto languageTypeDto; + private LanguageTypeDto language; @NotNull(message = "Proficiency level is required") @Valid - private ProficiencyLevelDto proficiencyLevelDto; + private ProficiencyLevelDto proficiency; @NotNull(message = "Student's list is required") @Valid - private List<String> studentIds; + private List<Long> studentIds; - public CourseDto(String name, Integer capacity, LanguageTypeDto languageTypeDto, ProficiencyLevelDto proficiencyLevelDto) { - setId("0"); + public CourseDto(Long id, String name, Integer capacity, LanguageTypeDto languageTypeDto, ProficiencyLevelDto proficiencyLevelDto) { + this.setId(id); this.name = name; this.capacity = capacity; - this.languageTypeDto = languageTypeDto; - this.proficiencyLevelDto = proficiencyLevelDto; + this.language = languageTypeDto; + this.proficiency = proficiencyLevelDto; this.studentIds = new ArrayList<>(); } } diff --git a/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureDto.java b/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureDto.java index df9b17a7..f54ac383 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureDto.java @@ -36,7 +36,7 @@ public class LectureDto extends DomainObjectDto { private String courseId; @NotNull(message = "Student IDs list cannot be null") - private List<String> studentIds; + private List<Long> studentIds; public LectureDto(LocalDateTime from, LocalDateTime to, String topic, Integer capacity, String lecturerId, String courseId) { this.from = from; diff --git a/application/model/src/main/java/org/fuseri/model/dto/user/UserDto.java b/application/model/src/main/java/org/fuseri/model/dto/user/UserDto.java index fce12aab..4d6eba73 100644 --- a/application/model/src/main/java/org/fuseri/model/dto/user/UserDto.java +++ b/application/model/src/main/java/org/fuseri/model/dto/user/UserDto.java @@ -1,10 +1,9 @@ package org.fuseri.model.dto.user; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import org.fuseri.model.dto.common.DomainObjectDto; import lombok.Getter; import lombok.Setter; +import org.fuseri.model.dto.common.DomainObjectDto; @Getter @Setter @@ -21,7 +20,7 @@ public class UserDto extends DomainObjectDto { public UserDto(String username, String email, String firstName, String lastName, AddressDto address) { - setId("0"); + setId(0L); this.username = username; this.email = email; this.firstName = firstName; diff --git a/application/module-language-school/pom.xml b/application/module-language-school/pom.xml index 42e3a5de..76585929 100644 --- a/application/module-language-school/pom.xml +++ b/application/module-language-school/pom.xml @@ -59,6 +59,10 @@ <version>0.0.1-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-tx</artifactId> + </dependency> </dependencies> diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainObject.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainObject.java index 8520e8aa..078871d9 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainObject.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainObject.java @@ -1,18 +1,19 @@ package org.fuseri.modulelanguageschool.common; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; import lombok.Getter; import lombok.Setter; -import java.util.UUID; - @Getter @Setter @MappedSuperclass public abstract class DomainObject { @Id - private String id = UUID.randomUUID().toString(); + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; } diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainService.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainService.java index 706071fb..426ef5c3 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainService.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/DomainService.java @@ -10,7 +10,7 @@ public abstract class DomainService<T extends DomainObject> { public static final int DEFAULT_PAGE_SIZE = 10; - public abstract JpaRepository<T, String> getRepository(); + public abstract JpaRepository<T, Long> getRepository(); @Transactional public T create(T entity) { diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/ResourceNotFoundException.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/ResourceNotFoundException.java new file mode 100644 index 00000000..2227c564 --- /dev/null +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/common/ResourceNotFoundException.java @@ -0,0 +1,22 @@ +package org.fuseri.modulelanguageschool.common; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException() { + } + + public ResourceNotFoundException(String message) { + super(message); + } + + public ResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public ResourceNotFoundException(Throwable cause) { + super(cause); + } + + public ResourceNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/Course.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/Course.java index 38f42dfa..455637c7 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/Course.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/Course.java @@ -3,11 +3,14 @@ package org.fuseri.modulelanguageschool.course; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; import lombok.*; import org.fuseri.modulelanguageschool.common.DomainObject; @Getter @Setter +@Entity +@Table(name = "course") @NoArgsConstructor @AllArgsConstructor public class Course extends DomainObject { @@ -19,5 +22,5 @@ public class Course extends DomainObject { private Language language; @Enumerated(EnumType.STRING) - private ProficiencyLevel proficiencyLevel; + private ProficiencyLevel proficiency; } 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 9646fb8f..75e92aa7 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,12 +1,15 @@ package org.fuseri.modulelanguageschool.course; import jakarta.validation.Valid; -import jakarta.validation.constraints.PositiveOrZero; import org.fuseri.model.dto.course.CourseCreateDto; import org.fuseri.model.dto.course.CourseDto; import org.fuseri.model.dto.course.LanguageTypeDto; import org.fuseri.model.dto.course.ProficiencyLevelDto; import org.fuseri.model.dto.user.UserDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; @@ -21,6 +24,12 @@ import java.util.List; public class CourseController { public static final String COURSE_NAME = "english b2 course"; + private final CourseFacade courseFacade; + + @Autowired + public CourseController(CourseFacade courseFacade) { + this.courseFacade = courseFacade; + } /** * Creates a new course. @@ -30,7 +39,7 @@ public class CourseController { */ @PostMapping("/create") public CourseDto create(@Valid @RequestBody CourseCreateDto dto) { - return new CourseDto(dto.getName(), dto.getCapacity(), dto.getLanguageTypeDto(), dto.getProficiencyLevelDto()); + return courseFacade.create(dto); } /** @@ -40,8 +49,8 @@ public class CourseController { * @return the CourseDto for the specified ID */ @GetMapping("/find/{id}") - public CourseDto find(@PathVariable String id) { - return new CourseDto(COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + public CourseDto find(@PathVariable Long id) { + return courseFacade.findById(id); } /** @@ -51,8 +60,8 @@ public class CourseController { * @return the Result containing the requested page of CourseDtos */ @GetMapping("/findAll") - public List<CourseDto> findAll(@RequestParam int page) { - return new ArrayList<>(); + public Page<CourseDto> findAll(@RequestParam int page) { + return courseFacade.findAll(PageRequest.of(page, 10, Sort.by(Sort.Direction.ASC, "name"))); } /** @@ -90,18 +99,18 @@ public class CourseController { * @return the updated CourseDto */ @PutMapping("/update/{id}") - public CourseDto update(@PathVariable String id, @Valid @RequestBody CourseCreateDto dto) { - return new CourseDto(dto.getName(), dto.getCapacity(), dto.getLanguageTypeDto(), dto.getProficiencyLevelDto()); + public CourseDto update(@PathVariable Long id, @Valid @RequestBody CourseCreateDto dto) { + return courseFacade.update(id, dto); } /** * Deletes a course by ID. * * @param id the ID of the course to delete - * @return true if the course was successfully deleted, false otherwise */ @DeleteMapping("/delete/{id}") - public void delete(@PathVariable String id) { + public void delete(@PathVariable Long id) { + courseFacade.delete(id); } @@ -113,8 +122,8 @@ public class CourseController { * @return the CourseDto representing the updated course */ @PatchMapping("/enrol/{id}") - public CourseDto enrol(@PathVariable String id, @RequestBody UserDto student) { - var course = new CourseDto(COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + public CourseDto enrol(@PathVariable Long id, @RequestBody UserDto student) { + var course = new CourseDto(0L, COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); course.setStudentIds(new ArrayList<>(List.of(student.getId()))); return course; } @@ -127,8 +136,8 @@ public class CourseController { * @return the CourseDto representing the updated course */ @PatchMapping("/expel/{id}") - public CourseDto expel(@PathVariable String id, @RequestBody UserDto student) { - return new CourseDto(COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + public CourseDto expel(@PathVariable Long id, @RequestBody UserDto student) { + return new CourseDto(0L, COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); } } 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 new file mode 100644 index 00000000..9899db25 --- /dev/null +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseFacade.java @@ -0,0 +1,49 @@ +package org.fuseri.modulelanguageschool.course; + +import org.fuseri.model.dto.course.CourseCreateDto; +import org.fuseri.model.dto.course.CourseDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +public class CourseFacade { + private final CourseService courseService; + private final CourseMapper courseMapper; + + @Autowired + public CourseFacade(CourseService courseService, CourseMapper courseMapper) { + this.courseService = courseService; + this.courseMapper = courseMapper; + } + + @Transactional + public CourseDto create(CourseCreateDto dto) { + return courseMapper.mapToDto(courseService.save(courseMapper.mapToCourse(dto))); + } + + @Cacheable(cacheNames = "courses", key = "#id") + @Transactional(readOnly = true) + public CourseDto findById(Long id) { + return courseMapper.mapToDto(courseService.findById(id)); + } + + @Transactional(readOnly = true) + public Page<CourseDto> findAll(Pageable pageable) { + return courseMapper.mapToPageDto(courseService.findAll(pageable)); + } + + @Transactional + public CourseDto update(Long id, CourseCreateDto dto) { + return courseMapper.mapToDto(courseService.update(id, courseMapper.mapToCourse(dto))); + } + + @Transactional + public void delete(Long id) { + courseService.delete(id); + } +} diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseMapper.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseMapper.java new file mode 100644 index 00000000..9a5bf7c0 --- /dev/null +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseMapper.java @@ -0,0 +1,27 @@ +package org.fuseri.modulelanguageschool.course; + +import org.fuseri.model.dto.course.CourseCreateDto; +import org.fuseri.model.dto.course.CourseDto; +import org.mapstruct.Mapper; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; + +import java.util.List; + +@Mapper(componentModel = "spring") +public interface CourseMapper { + + CourseDto mapToDto(Course course); + + Course mapToCourse(CourseDto courseDto); + + List<CourseDto> mapToList(List<Course> persons); + + + default Page<CourseDto> mapToPageDto(Page<Course> courses) { + return new PageImpl<>(mapToList(courses.getContent()), courses.getPageable(), courses.getTotalPages()); + } + + Course mapToCourse(CourseCreateDto dto); +} + diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseRepository.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseRepository.java new file mode 100644 index 00000000..1dcad446 --- /dev/null +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseRepository.java @@ -0,0 +1,9 @@ +package org.fuseri.modulelanguageschool.course; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CourseRepository extends JpaRepository<Course, Long> { + +} diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseService.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseService.java new file mode 100644 index 00000000..eb206585 --- /dev/null +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/course/CourseService.java @@ -0,0 +1,57 @@ +package org.fuseri.modulelanguageschool.course; + +import org.fuseri.modulelanguageschool.common.ResourceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +@Service +public class CourseService { + + private final CourseRepository courseRepository; + + @Autowired + public CourseService(CourseRepository courseRepository) { + this.courseRepository = courseRepository; + } + + @Transactional + public Course save(Course course) { + return courseRepository.save(course); + } + + @Transactional(readOnly = true) + public Course findById(Long id) { + return courseRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Course with id: " + id + " was not found.")); + } + + @Transactional(readOnly = true) + public Page<Course> findAll(Pageable pageable) { + return courseRepository.findAll(pageable); + } + + @Transactional + public Course update(Long id, Course newCourse) { + Optional<Course> optionalCourse = courseRepository.findById(id); + if (optionalCourse.isPresent()) { + Course course = optionalCourse.get(); + course.setName(newCourse.getName()); + course.setCapacity(newCourse.getCapacity()); + course.setLanguage(newCourse.getLanguage()); + course.setProficiency(newCourse.getProficiency()); + return courseRepository.save(course); + } else { + throw new ResourceNotFoundException("Course with id: " + id + " was not found."); + } + } + + @Transactional + public void delete(Long id) { + courseRepository.deleteById(id); + } +} diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java index d0c6429b..6164c8e6 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserController.java @@ -25,13 +25,13 @@ public class UserController { private final UserMapper mapper; @Autowired - public UserController(UserService service/*, UserMapper mapper*/) { + public UserController(UserService service, UserMapper mapper) { this.service = service; - this.mapper = null; + this.mapper = mapper; } @GetMapping("/{id}") - public UserDto find(@PathVariable String id) { + public UserDto find(@PathVariable Long id) { return new UserDto("spracher","spracher@gmail.com","Sprach","MeNot",new AddressDto()); } @@ -41,12 +41,12 @@ public class UserController { } @DeleteMapping("/{id}") - public UserDto deleteUser(@PathVariable String id) { + public UserDto deleteUser(@PathVariable Long id) { return new UserDto("spracher","spracher@gmail.com","Sprach","MeNot",new AddressDto()); } @PutMapping("/update/{id}") - public UserDto update(@PositiveOrZero @PathVariable String id,@Valid @RequestBody UserCreateDto user) { + public UserDto update(@PositiveOrZero @PathVariable Long id,@Valid @RequestBody UserCreateDto user) { return new UserDto(user.getUsername(),user.getEmail(),user.getFirstName(),user.getLastName(),user.getAddress()); } @@ -68,22 +68,22 @@ public class UserController { @PostMapping("/logout/{id}") - public String logout(@PathVariable String id) { + public String logout(@PathVariable Long id) { return "user has logged out"; } @GetMapping("/finished/{id}") - public List<Course> getFinished(@PathVariable String id) { + public List<Course> getFinished(@PathVariable Long id) { return new ArrayList<>(); } @GetMapping("/enrolled/{id}") - public List<Course> getEnrolled(@PathVariable String id) { + public List<Course> getEnrolled(@PathVariable Long id) { return new ArrayList<>(); } @PutMapping("/addLanguage/{id}") - public String addLanguage(@PathVariable String id,@Valid @RequestBody UserAddLanguageDto body) { + public String addLanguage(@PathVariable Long id,@Valid @RequestBody UserAddLanguageDto body) { return "added Absolutely Nothing successfully!"; } diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserRepository.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserRepository.java index 32d06b13..f649fbd4 100644 --- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserRepository.java +++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/user/UserRepository.java @@ -4,5 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface UserRepository extends JpaRepository<User, String> { +public interface UserRepository extends JpaRepository<User, Long> { } 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 1fc03b8c..12853d56 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 @@ -19,7 +19,7 @@ public class UserService extends DomainService<User> { } @Transactional(readOnly = true) - public User find(String id) { + public User find(Long id) { return repository.findById(id) .orElseThrow(() -> new EntityNotFoundException("User '" + id + "' not found.")); } diff --git a/application/module-language-school/src/main/resources/application.properties b/application/module-language-school/src/main/resources/application.properties index 888dcec4..08d8fcc5 100644 --- a/application/module-language-school/src/main/resources/application.properties +++ b/application/module-language-school/src/main/resources/application.properties @@ -1 +1,13 @@ -server.port=5000 \ No newline at end of file +server.port=5000 + +spring.jpa.open-in-view=false +spring.datasource.url=jdbc:h2:mem:social-network;MODE=PostgreSQL +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=SedaQ-app +spring.datasource.password=$argon2id$v=19$m=16,t=2,p=1$YmF0bWFuYmF0bWFu$MdHYB359HdivAb9J6CaILw +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +# showing SQL is generally good practice for running project locally to check whether there is not an issue with implementation of JPA methods. +spring.jpa.show-sql=true +spring.jackson.property-naming-strategy=LOWER_CAMEL_CASE +spring.cache.type=NONE +appconfig.enablecache=false \ No newline at end of file diff --git a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/course/CourseTest.java b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/course/CourseTest.java index e4fd8f0e..05add7ac 100644 --- a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/course/CourseTest.java +++ b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/course/CourseTest.java @@ -14,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageImpl; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -21,7 +22,8 @@ import java.util.ArrayList; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -29,15 +31,24 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc public class CourseTest { + + private final CourseCreateDto courseCreateDto = new CourseCreateDto("english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + private final CourseDto courseDto = new CourseDto(0L, "english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); @Autowired ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; + @MockBean private CourseController courseController; - private final CourseCreateDto courseCreateDto = new CourseCreateDto("english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2);; - private final CourseDto courseDto = new CourseDto("english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + public static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } @Test void createCourse() throws Exception { @@ -48,8 +59,8 @@ public class CourseTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("english b2 course")) .andExpect(jsonPath("$.capacity").value(10)) - .andExpect(jsonPath("$.languageTypeDto").value("ENGLISH")) - .andExpect(jsonPath("$.proficiencyLevelDto").value("B2")) + .andExpect(jsonPath("$.language").value("ENGLISH")) + .andExpect(jsonPath("$.proficiency").value("B2")) .andExpect(jsonPath("$.id").exists()) .andReturn().getResponse().getContentAsString(); } @@ -74,21 +85,21 @@ public class CourseTest { @Test void findCourse() throws Exception { - String id = "0"; + Long id = 0L; Mockito.when(courseController.find(id)).thenReturn(courseDto); mockMvc.perform(get("/courses/find/" + id)) .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(id)) .andExpect(jsonPath("$.name").value("english b2 course")) .andExpect(jsonPath("$.capacity").value(10)) - .andExpect(jsonPath("$.languageTypeDto").value("ENGLISH")) - .andExpect(jsonPath("$.proficiencyLevelDto").value("B2")) - .andExpect(jsonPath("$.id").value(id)) + .andExpect(jsonPath("$.language").value("ENGLISH")) + .andExpect(jsonPath("$.proficiency").value("B2")) .andReturn().getResponse().getContentAsString(); } @Test void findCourseWithoutId() throws Exception { - Mockito.when(courseController.find(ArgumentMatchers.anyString())).thenReturn(courseDto); + Mockito.when(courseController.find(ArgumentMatchers.anyLong())).thenReturn(courseDto); mockMvc.perform(get("/courses/find/")) .andExpect(status().is4xxClientError()); } @@ -96,18 +107,18 @@ public class CourseTest { @Test void findAll() throws Exception { int page = 0; - Mockito.when(courseController.findAll(page)).thenReturn(new ArrayList<>()); + Mockito.when(courseController.findAll(page)).thenReturn(new PageImpl<>(new ArrayList<>())); String response = mockMvc.perform(get("/courses/findAll").param("page", Integer.toString(page))) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); - assertThat("response", response, is("[]")); + assertTrue(response.contains("\"content\":[]")); } @Test void findAllWithoutPage() throws Exception { - Mockito.when(courseController.findAll(ArgumentMatchers.anyInt())).thenReturn(new ArrayList<>()); - mockMvc.perform(get("/courses/findAll")) +// Mockito.when(courseController.findAll(ArgumentMatchers.anyInt())).thenReturn(new PageImpl<>(new ArrayList<>())); + mockMvc.perform(get("/courses/findAll")) .andExpect(status().is4xxClientError()); } @@ -128,7 +139,7 @@ public class CourseTest { @Test void findAllByLangWithoutParameters() throws Exception { Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(), - ArgumentMatchers.isA(LanguageTypeDto.class))) + ArgumentMatchers.isA(LanguageTypeDto.class))) .thenReturn(new ArrayList<>()); mockMvc.perform(get("/courses/findAllByLang")) .andExpect(status().is4xxClientError()); @@ -138,7 +149,7 @@ public class CourseTest { void findAllByLangWithoutLang() throws Exception { Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(), ArgumentMatchers.isA(LanguageTypeDto.class))) - .thenReturn(new ArrayList<>()); + .thenReturn(new ArrayList<>()); String page = "0"; mockMvc.perform(get("/courses/findAllByLang").param("page", page)) .andExpect(status().is4xxClientError()); @@ -183,26 +194,26 @@ public class CourseTest { @Test void updateCourse() throws Exception { - String id = "0"; + Long id = 0L; Mockito.when(courseController.update(ArgumentMatchers.eq(id), - ArgumentMatchers.isA(CourseCreateDto.class))) + ArgumentMatchers.isA(CourseCreateDto.class))) .thenReturn(courseDto); mockMvc.perform(put("/courses/update/" + id) - .content(asJsonString(courseDto)) - .contentType(MediaType.APPLICATION_JSON)) + .content(asJsonString(courseDto)) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("english b2 course")) .andExpect(jsonPath("$.capacity").value(10)) - .andExpect(jsonPath("$.languageTypeDto").value("ENGLISH")) - .andExpect(jsonPath("$.proficiencyLevelDto").value("B2")) + .andExpect(jsonPath("$.language").value("ENGLISH")) + .andExpect(jsonPath("$.proficiency").value("B2")) .andExpect(jsonPath("$.id").value(courseDto.getId())) .andReturn().getResponse().getContentAsString(); } @Test void updateCourseWithoutParameter() throws Exception { - Mockito.when(courseController.update(ArgumentMatchers.anyString(), + Mockito.when(courseController.update(ArgumentMatchers.anyLong(), ArgumentMatchers.isA(CourseCreateDto.class))) .thenReturn(courseDto); mockMvc.perform(put("/courses/update")) @@ -211,7 +222,7 @@ public class CourseTest { @Test void deleteCourse() throws Exception { - String id = "0"; + Long id = 0L; Mockito.doNothing().when(courseController).delete(id); mockMvc.perform(delete("/courses/delete/" + id)) @@ -220,7 +231,7 @@ public class CourseTest { @Test void deleteCourseWithoutParameter() throws Exception { - Mockito.doNothing().when(courseController).delete(ArgumentMatchers.anyString()); + Mockito.doNothing().when(courseController).delete(ArgumentMatchers.anyLong()); mockMvc.perform(delete("/courses/delete/")) .andExpect(status().is4xxClientError()); @@ -228,11 +239,11 @@ public class CourseTest { @Test void enrolCourse() throws Exception { - String id = "0"; - UserDto student = new UserDto("novakovat","novakova@gamil.com", "Tereza", + Long id = 0L; + UserDto student = new UserDto("novakovat", "novakova@gamil.com", "Tereza", "Nováková", new AddressDto()); - CourseDto courseDtoWithStudent = new CourseDto("english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); + CourseDto courseDtoWithStudent = new CourseDto(id, "english b2 course", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2); courseDtoWithStudent.setStudentIds(new ArrayList<>(List.of(student.getId()))); Mockito.when(courseController.enrol(ArgumentMatchers.eq(id), @@ -244,16 +255,16 @@ public class CourseTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("english b2 course")) .andExpect(jsonPath("$.capacity").value(10)) - .andExpect(jsonPath("$.languageTypeDto").value("ENGLISH")) - .andExpect(jsonPath("$.proficiencyLevelDto").value("B2")) - .andExpect(jsonPath("$.studentIds").value(student.getId())) + .andExpect(jsonPath("$.language").value("ENGLISH")) + .andExpect(jsonPath("$.proficiency").value("B2")) + .andExpect(jsonPath("$.studentIds").exists()) .andReturn().getResponse().getContentAsString(); } @Test void enrolCourseWithoutUserParameter() throws Exception { String id = "0"; - Mockito.when(courseController.enrol(ArgumentMatchers.anyString(), + Mockito.when(courseController.enrol(ArgumentMatchers.anyLong(), ArgumentMatchers.isA(UserDto.class))) .thenReturn(courseDto); mockMvc.perform(patch("/courses/enrol/" + id)) @@ -262,26 +273,26 @@ public class CourseTest { @Test void enrolCourseWithoutCourseIdParameter() throws Exception { - Mockito.when(courseController.enrol(ArgumentMatchers.anyString(), + Mockito.when(courseController.enrol(ArgumentMatchers.anyLong(), ArgumentMatchers.isA(UserDto.class))) .thenReturn(courseDto); - UserDto student = new UserDto("novakovat","novakova@gamil.com", "Tereza", + UserDto student = new UserDto("novakovat", "novakova@gamil.com", "Tereza", "Nováková", new AddressDto()); mockMvc.perform(patch("/courses/enrol/") - .content(asJsonString(student)) - .contentType(MediaType.APPLICATION_JSON)) + .content(asJsonString(student)) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is4xxClientError()); } @Test void expelCourse() throws Exception { - String id = "0"; + Long id = 0L; Mockito.when(courseController.expel(ArgumentMatchers.eq(id), ArgumentMatchers.isA(UserDto.class))) .thenReturn(courseDto); - UserDto student = new UserDto("novakovat","novakova@gamil.com", "Tereza", + UserDto student = new UserDto("novakovat", "novakova@gamil.com", "Tereza", "Nováková", new AddressDto()); mockMvc.perform(patch("/courses/expel/" + id) @@ -290,15 +301,15 @@ public class CourseTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("english b2 course")) .andExpect(jsonPath("$.capacity").value(10)) - .andExpect(jsonPath("$.languageTypeDto").value("ENGLISH")) - .andExpect(jsonPath("$.proficiencyLevelDto").value("B2")) + .andExpect(jsonPath("$.language").value("ENGLISH")) + .andExpect(jsonPath("$.proficiency").value("B2")) .andExpect(jsonPath("$.studentIds").isEmpty()) .andReturn().getResponse().getContentAsString(); } @Test void expelCourseWithoutUserParameter() throws Exception { - String id = "0"; + Long id = 0L; Mockito.when(courseController.expel(ArgumentMatchers.eq(id), ArgumentMatchers.isA(UserDto.class))) .thenReturn(courseDto); @@ -309,10 +320,10 @@ public class CourseTest { @Test void deleteCourseWithoutCourseIdParameter() throws Exception { - Mockito.when(courseController.expel(ArgumentMatchers.anyString(), + Mockito.when(courseController.expel(ArgumentMatchers.anyLong(), ArgumentMatchers.isA(UserDto.class))) .thenReturn(courseDto); - UserDto student = new UserDto("novakovat","novakova@gamil.com", "Tereza", + UserDto student = new UserDto("novakovat", "novakova@gamil.com", "Tereza", "Nováková", new AddressDto()); mockMvc.perform(patch("/courses/expel/") @@ -320,12 +331,4 @@ public class CourseTest { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is4xxClientError()); } - - public static String asJsonString(final Object obj) { - try { - return new ObjectMapper().writeValueAsString(obj); - } catch (Exception e) { - throw new RuntimeException(e); - } - } } diff --git a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java index 85aa5f33..59f5032b 100644 --- a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java +++ b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/user/UserControllerTest.java @@ -121,7 +121,7 @@ class UserControllerTest { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); - String id = objectMapper.readValue(response, UserDto.class).getId(); + Long id = objectMapper.readValue(response, UserDto.class).getId(); mockMvc.perform(get("/users/{id}", id)) .andExpect(status().isOk()) @@ -142,7 +142,7 @@ class UserControllerTest { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); - String id = objectMapper.readValue(response, UserDto.class).getId(); + Long id = objectMapper.readValue(response, UserDto.class).getId(); mockMvc.perform(delete("/users/{id}", id) .contentType(MediaType.APPLICATION_JSON)) @@ -156,7 +156,7 @@ class UserControllerTest { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); - String id = objectMapper.readValue(response, UserDto.class).getId(); + Long id = objectMapper.readValue(response, UserDto.class).getId(); var updatedUsername = "novak"; var userToUpdate = new UserCreateDto( @@ -195,7 +195,7 @@ class UserControllerTest { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); - String id = objectMapper.readValue(response, UserDto.class).getId(); + Long id = objectMapper.readValue(response, UserDto.class).getId(); mockMvc.perform(post("/users/logout/{id}", id)) .andExpect(status().isOk()); @@ -203,19 +203,19 @@ class UserControllerTest { @Test void getFinished() throws Exception { - mockMvc.perform(get("/users/finished/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af")) + mockMvc.perform(get("/users/finished/1", "1c1bbf66-6585-4978-886b-b126335ff3af")) .andExpect(status().isOk()); } @Test void getEnrolled() throws Exception { - mockMvc.perform(get("/users/enrolled/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af")) + mockMvc.perform(get("/users/enrolled/1", "1c1bbf66-6585-4978-886b-b126335ff3af")) .andExpect(status().isOk()); } @Test void addLanguage() throws Exception { - mockMvc.perform(put("/users/addLanguage/{id}", "1c1bbf66-6585-4978-886b-b126335ff3af") + mockMvc.perform(put("/users/addLanguage/1", "1c1bbf66-6585-4978-886b-b126335ff3af") .content(asJsonString(new UserAddLanguageDto(LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2))) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); diff --git a/application/module-language-school/src/test/resources/application-test.properties b/application/module-language-school/src/test/resources/application-test.properties new file mode 100644 index 00000000..896213ef --- /dev/null +++ b/application/module-language-school/src/test/resources/application-test.properties @@ -0,0 +1,14 @@ +# For description of each field check: https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# INIT=CREATE SCHEMA IF NOT EXISTS PA165;SET SCHEMA PA165 +spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.username=SedaQ-app-test +spring.datasource.password= +spring.datasource.driverClassName=org.h2.Driver +spring.jpa.hibernate.ddl-auto=create +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect +spring.jpa.properties.hibernate.generate_statistics=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.show_sql=false +spring.h2.console.enabled=true +spring.cache.type=NONE +appconfig.enablecache=false \ No newline at end of file diff --git a/application/module-language-school/src/test/resources/logback.xml b/application/module-language-school/src/test/resources/logback.xml new file mode 100644 index 00000000..e449f9cd --- /dev/null +++ b/application/module-language-school/src/test/resources/logback.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%d %5p %40.40c:%4L - %m%n</pattern> + </encoder> + </appender> + + <root level="info"> + <appender-ref ref="console" /> + </root> +<!-- TODO remove solution--> + <logger name="org.hibernate.SQL" level="DEBUG"/> + +</configuration> \ No newline at end of file -- GitLab