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 1/4] 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


From 6cc75aa61d5b061a8df5ed5e9ff2b61c38a24d54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <xpokorn8@fi.muni.cz>
Date: Wed, 5 Apr 2023 22:05:18 +0200
Subject: [PATCH 2/4] Course implementation

---
 .../model/dto/common/DomainObjectDto.java     |  2 --
 .../modulelanguageschool/course/Course.java   | 20 +++++++++---
 .../course/CourseController.java              | 18 ++++-------
 .../course/CourseFacade.java                  | 26 ++++++++++++++-
 .../course/CourseRepository.java              |  8 +++++
 .../course/CourseService.java                 | 32 +++++++++++++++++++
 .../course/CourseTest.java                    | 25 +++------------
 7 files changed, 92 insertions(+), 39 deletions(-)

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 36ec98b0..7824c73c 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
@@ -1,6 +1,5 @@
 package org.fuseri.model.dto.common;
 
-import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
@@ -9,7 +8,6 @@ import lombok.Setter;
 @Setter
 public abstract class DomainObjectDto {
 
-    @NotBlank
     @NotNull
     private Long id;
 }
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 455637c7..99e1ff83 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
@@ -1,11 +1,12 @@
 package org.fuseri.modulelanguageschool.course;
 
-import jakarta.persistence.Entity;
-import jakarta.persistence.EnumType;
-import jakarta.persistence.Enumerated;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
 import lombok.*;
 import org.fuseri.modulelanguageschool.common.DomainObject;
+import org.fuseri.modulelanguageschool.user.User;
+
+import java.util.List;
+import java.util.Set;
 
 @Getter
 @Setter
@@ -23,4 +24,15 @@ public class Course extends DomainObject {
 
     @Enumerated(EnumType.STRING)
     private ProficiencyLevel proficiency;
+
+    @ManyToMany
+    private Set<User> students;
+
+    public void enrolStudent(User student) {
+        students.add(student);
+    }
+
+    public void expelStudent(User student) {
+        students.remove(student);
+    }
 }
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 75e92aa7..6d11099e 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
@@ -23,7 +23,6 @@ import java.util.List;
 @RequestMapping("/courses")
 public class CourseController {
 
-    public static final String COURSE_NAME = "english b2 course";
     private final CourseFacade courseFacade;
 
     @Autowired
@@ -67,28 +66,25 @@ public class CourseController {
     /**
      * Retrieves a paginated list of courses of a given language
      *
-     * @param page the page number to retrieve
      * @param lang the language to find courses of
      * @return the Result containing the requested page of CourseDtos
      */
     @GetMapping("/findAllByLang")
-    public List<CourseDto> findAll(@RequestParam int page, @RequestParam LanguageTypeDto lang) {
-        return new ArrayList<>();
+    public List<CourseDto> findAll(@RequestParam LanguageTypeDto lang) {
+        return courseFacade.findAll(lang);
     }
 
     /**
      * Retrieves a paginated list of courses of a given language and proficiency
      *
-     * @param page the page number to retrieve
      * @param lang the language to find courses of
      * @param prof the proficiency of the language
      * @return the Result containing the requested page of CourseDtos
      */
     @GetMapping("/findAllByLangProf")
-    public List<CourseDto> findAll(@RequestParam int page,
-                                   @RequestParam LanguageTypeDto lang,
+    public List<CourseDto> findAll(@RequestParam LanguageTypeDto lang,
                                    @RequestParam ProficiencyLevelDto prof) {
-        return new ArrayList<>();
+        return courseFacade.findAll(lang, prof);
     }
 
     /**
@@ -123,9 +119,7 @@ public class CourseController {
      */
     @PatchMapping("/enrol/{id}")
     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;
+        return courseFacade.enrol(id, student);
     }
 
     /**
@@ -137,7 +131,7 @@ public class CourseController {
      */
     @PatchMapping("/expel/{id}")
     public CourseDto expel(@PathVariable Long id, @RequestBody UserDto student) {
-        return new CourseDto(0L, COURSE_NAME, 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.B2);
+        return courseFacade.expel(id, student);
     }
 
 }
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 9899db25..db7f28aa 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
@@ -2,6 +2,10 @@ package org.fuseri.modulelanguageschool.course;
 
 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.fuseri.modulelanguageschool.user.UserMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.domain.Page;
@@ -9,16 +13,20 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 @Service
 @Transactional
 public class CourseFacade {
     private final CourseService courseService;
     private final CourseMapper courseMapper;
+    private final UserMapper userMapper;
 
     @Autowired
-    public CourseFacade(CourseService courseService, CourseMapper courseMapper) {
+    public CourseFacade(CourseService courseService, CourseMapper courseMapper, UserMapper userMapper) {
         this.courseService = courseService;
         this.courseMapper = courseMapper;
+        this.userMapper = userMapper;
     }
 
     @Transactional
@@ -46,4 +54,20 @@ public class CourseFacade {
     public void delete(Long id) {
         courseService.delete(id);
     }
+
+    public List<CourseDto> findAll(LanguageTypeDto lang) {
+        return courseMapper.mapToList(courseService.findAll(Language.valueOf(lang.name())));
+    }
+
+    public List<CourseDto> findAll(LanguageTypeDto lang, ProficiencyLevelDto prof) {
+        return courseMapper.mapToList(courseService.findAll(Language.valueOf(lang.name()), ProficiencyLevel.valueOf(prof.name())));
+    }
+
+    public CourseDto enrol(Long id, UserDto student) {
+        return courseMapper.mapToDto(courseService.enrol(id, userMapper.fromDto(student)));
+    }
+
+    public CourseDto expel(Long id, UserDto student) {
+        return courseMapper.mapToDto(courseService.expel(id, userMapper.fromDto(student)));
+    }
 }
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
index 1dcad446..4ea630cc 100644
--- 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
@@ -1,9 +1,17 @@
 package org.fuseri.modulelanguageschool.course;
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
+
 @Repository
 public interface CourseRepository extends JpaRepository<Course, Long> {
 
+    @Query("SELECT c FROM Course c WHERE c.language = ?1")
+    List<Course> findAllByLang(Language language);
+
+    @Query("SELECT c FROM Course c WHERE c.language = ?1 AND c.proficiency = ?2")
+    List<Course> findAllByLangProf(Language language, ProficiencyLevel proficiencyLevel);
 }
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
index eb206585..13079299 100644
--- 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
@@ -1,12 +1,14 @@
 package org.fuseri.modulelanguageschool.course;
 
 import org.fuseri.modulelanguageschool.common.ResourceNotFoundException;
+import org.fuseri.modulelanguageschool.user.User;
 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.List;
 import java.util.Optional;
 
 @Service
@@ -54,4 +56,34 @@ public class CourseService {
     public void delete(Long id) {
         courseRepository.deleteById(id);
     }
+
+    public List<Course> findAll(Language language) {
+        return courseRepository.findAllByLang(language);
+    }
+
+    public List<Course> findAll(Language language, ProficiencyLevel proficiencyLevel) {
+        return courseRepository.findAllByLangProf(language, proficiencyLevel);
+    }
+
+    public Course enrol(Long id, User student) {
+        Optional<Course> optionalCourse = courseRepository.findById(id);
+        if (optionalCourse.isPresent()) {
+            Course course = optionalCourse.get();
+            course.enrolStudent(student);
+            return courseRepository.save(course);
+        } else {
+            throw new ResourceNotFoundException("Course with id: " + id + " was not found.");
+        }
+    }
+
+    public Course expel(Long id, User student) {
+        Optional<Course> optionalCourse = courseRepository.findById(id);
+        if (optionalCourse.isPresent()) {
+            Course course = optionalCourse.get();
+            course.expelStudent(student);
+            return courseRepository.save(course);
+        } else {
+            throw new ResourceNotFoundException("Course with id: " + id + " was not found.");
+        }
+    }
 }
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 05add7ac..bab37001 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
@@ -126,7 +126,7 @@ public class CourseTest {
     void findAllByLang() throws Exception {
         int page = 0;
         LanguageTypeDto lang = LanguageTypeDto.ENGLISH;
-        Mockito.when(courseController.findAll(page, lang)).thenReturn(new ArrayList<>());
+        Mockito.when(courseController.findAll(lang)).thenReturn(new ArrayList<>());
         String response = mockMvc.perform(get("/courses/findAllByLang")
                         .param("page", Integer.toString(page))
                         .param("lang", lang.toString()))
@@ -136,33 +136,18 @@ public class CourseTest {
         assertThat("response", response, is("[]"));
     }
 
-    @Test
-    void findAllByLangWithoutParameters() throws Exception {
-        Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.isA(LanguageTypeDto.class)))
-                .thenReturn(new ArrayList<>());
-        mockMvc.perform(get("/courses/findAllByLang"))
-                .andExpect(status().is4xxClientError());
-    }
-
     @Test
     void findAllByLangWithoutLang() throws Exception {
-        Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.isA(LanguageTypeDto.class)))
-                .thenReturn(new ArrayList<>());
-        String page = "0";
-        mockMvc.perform(get("/courses/findAllByLang").param("page", page))
+        mockMvc.perform(get("/courses/findAllByLang"))
                 .andExpect(status().is4xxClientError());
     }
 
     @Test
     void findAllByLangProf() throws Exception {
-        int page = 0;
         LanguageTypeDto lang = LanguageTypeDto.ENGLISH;
         ProficiencyLevelDto proficiencyLevel = ProficiencyLevelDto.A1;
-        Mockito.when(courseController.findAll(page, lang, proficiencyLevel)).thenReturn(new ArrayList<>());
+        Mockito.when(courseController.findAll(lang, proficiencyLevel)).thenReturn(new ArrayList<>());
         String response = mockMvc.perform(get("/courses/findAllByLangProf")
-                        .param("page", Integer.toString(page))
                         .param("lang", lang.toString())
                         .param("prof", proficiencyLevel.toString()))
                 .andExpect(status().isOk())
@@ -173,7 +158,7 @@ public class CourseTest {
 
     @Test
     void findAllByLangProfWithoutParameters() throws Exception {
-        Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(),
+        Mockito.when(courseController.findAll(
                         ArgumentMatchers.isA(LanguageTypeDto.class),
                         ArgumentMatchers.isA(ProficiencyLevelDto.class)))
                 .thenReturn(new ArrayList<>());
@@ -183,7 +168,7 @@ public class CourseTest {
 
     @Test
     void findAllByLangProfWithoutLangProf() throws Exception {
-        Mockito.when(courseController.findAll(ArgumentMatchers.anyInt(),
+        Mockito.when(courseController.findAll(
                         ArgumentMatchers.isA(LanguageTypeDto.class),
                         ArgumentMatchers.isA(ProficiencyLevelDto.class)))
                 .thenReturn(new ArrayList<>());
-- 
GitLab


From e54e5b84cc71dcdf8f2b03149b654037bb031508 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <xpokorn8@fi.muni.cz>
Date: Fri, 7 Apr 2023 11:59:12 +0200
Subject: [PATCH 3/4] Lecture implementation

---
 .../model/dto/lecture/LectureCreateDto.java   |   7 +-
 .../fuseri/model/dto/lecture/LectureDto.java  |  10 +-
 .../modulelanguageschool/lecture/Lecture.java |  18 +++-
 .../lecture/LectureController.java            |  54 +++++-----
 .../lecture/LectureFacade.java                |  78 ++++++++++++++
 .../lecture/LectureMapper.java                |  25 +++++
 .../lecture/LectureRepository.java            |  21 ++++
 .../lecture/LectureService.java               | 102 ++++++++++++++++++
 .../lecture/LectureTest.java                  |  34 +++---
 9 files changed, 294 insertions(+), 55 deletions(-)
 create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java
 create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureMapper.java
 create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureRepository.java
 create mode 100644 application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureService.java

diff --git a/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureCreateDto.java b/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureCreateDto.java
index 6fc9b766..0279180b 100644
--- a/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureCreateDto.java
+++ b/application/model/src/main/java/org/fuseri/model/dto/lecture/LectureCreateDto.java
@@ -3,7 +3,6 @@ package org.fuseri.model.dto.lecture;
 import jakarta.validation.constraints.*;
 import lombok.Getter;
 import lombok.Setter;
-import org.fuseri.model.dto.course.CourseDto;
 
 import java.time.LocalDateTime;
 
@@ -27,10 +26,10 @@ public class LectureCreateDto {
     @Min(value = 1, message = "Lecture capacity must be at least 1")
     private Integer capacity;
 
-    @NotBlank(message = "Lecture course cannot be blank")
-    private String courseId;
+    @NotNull(message = "Lecture course cannot be null")
+    private Long courseId;
 
-    public LectureCreateDto(LocalDateTime from, LocalDateTime to, String topic, Integer capacity, String courseId) {
+    public LectureCreateDto(LocalDateTime from, LocalDateTime to, String topic, Integer capacity, Long courseId) {
         this.from = from;
         this.to = to;
         this.topic = topic;
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 f54ac383..df35d83c 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
@@ -29,16 +29,16 @@ public class LectureDto extends DomainObjectDto {
     @Min(value = 1, message = "Lecture capacity must be at least 1")
     private Integer capacity;
 
-    @NotNull(message = "Lecture capacity cannot be null")
-    private String lecturerId;
+    @NotNull(message = "Lecture lecturer cannot be null")
+    private Long lecturerId;
 
-    @NotBlank(message = "Lecture courseId cannot be blank")
-    private String courseId;
+    @NotNull(message = "Lecture courseId cannot be null")
+    private Long courseId;
 
     @NotNull(message = "Student IDs list cannot be null")
     private List<Long> studentIds;
 
-    public LectureDto(LocalDateTime from, LocalDateTime to, String topic, Integer capacity, String lecturerId, String courseId) {
+    public LectureDto(LocalDateTime from, LocalDateTime to, String topic, Integer capacity, Long lecturerId, Long courseId) {
         this.from = from;
         this.to = to;
         this.topic = topic;
diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/Lecture.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/Lecture.java
index 86b9670a..a719f242 100644
--- a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/Lecture.java
+++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/Lecture.java
@@ -3,6 +3,7 @@ package org.fuseri.modulelanguageschool.lecture;
 import jakarta.persistence.Entity;
 import jakarta.persistence.ManyToMany;
 import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
@@ -12,13 +13,15 @@ import org.fuseri.modulelanguageschool.course.Course;
 import org.fuseri.modulelanguageschool.user.User;
 
 import java.time.LocalDateTime;
-import java.util.List;
+import java.util.Set;
 
 /**
  * This class represents a lecture entity in the domain model.
  */
 @Getter
 @Setter
+@Entity
+@Table(name = "lecture")
 @NoArgsConstructor
 @AllArgsConstructor
 public class Lecture extends DomainObject {
@@ -27,9 +30,20 @@ public class Lecture extends DomainObject {
     private LocalDateTime to;
     private String topic;
 
+    @ManyToOne
     private Course course;
 
+    @ManyToOne
     private User lecturer;
 
-    private List<User> user;
+    @ManyToMany
+    private Set<User> students;
+
+    public void enrolStudent(User student) {
+        students.add(student);
+    }
+
+    public void expelStudent(User student) {
+        students.remove(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 34d91578..99da62d6 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,15 +1,12 @@
 package org.fuseri.modulelanguageschool.lecture;
 
 import jakarta.validation.Valid;
-import org.fuseri.model.dto.course.CourseDto;
 import org.fuseri.model.dto.lecture.LectureCreateDto;
 import org.fuseri.model.dto.lecture.LectureDto;
 import org.fuseri.model.dto.user.UserDto;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
-import java.time.LocalDateTime;
-import java.time.Month;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -20,9 +17,13 @@ import java.util.List;
 @RequestMapping("/lectures")
 public class LectureController {
 
-    private static final LocalDateTime START_DATETIME = LocalDateTime.of(2045, Month.JUNE, 30, 12, 0, 0);
-    private static final LocalDateTime END_DATETIME = LocalDateTime.of(2045, Month.JUNE, 30, 14, 0, 0);
-    public static final String TOPIC = "Learning how to spell deprecated";
+    private final LectureFacade lectureFacade;
+
+    @Autowired
+    public LectureController(LectureFacade lectureFacade) {
+        this.lectureFacade = lectureFacade;
+    }
+
 
     /**
      * Creates a new lecture resource by delegating to the LectureService's create method.
@@ -32,18 +33,18 @@ public class LectureController {
      */
     @PostMapping("/create")
     public LectureDto create(@Valid @RequestBody LectureCreateDto lecture) {
-        return new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
+        return lectureFacade.create(lecture);
     }
 
     /**
      * Retrieves a lecture resource by its ID.
      *
-     * @param id the ID of the lecture to find
+     * @param courseId the ID of the lecture to find
      * @return the LectureDto representing the found lecture
      */
-    @GetMapping("find/{id}")
-    public LectureDto find(@PathVariable String id) {
-        return new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
+    @GetMapping("find/{courseId}")
+    public LectureDto find(@PathVariable Long courseId) {
+        return lectureFacade.findById(courseId);
     }
 
     /**
@@ -53,8 +54,8 @@ public class LectureController {
      * @return the list of LectureDtos
      */
     @GetMapping("/findByCourse")
-    public List<LectureDto> findByCourse(@Valid @RequestParam String courseId) {
-        return new ArrayList<>();
+    public List<LectureDto> findByCourse(@Valid @RequestParam Long courseId) {
+        return lectureFacade.findAll(courseId);
     }
 
     /**
@@ -64,8 +65,8 @@ public class LectureController {
      * @return the LectureDto representing the updated lecture
      */
     @PutMapping("/update/{id}")
-    public LectureDto update(@PathVariable String id, @Valid @RequestBody LectureCreateDto lecture) {
-        return new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
+    public LectureDto update(@PathVariable Long id, @Valid @RequestBody LectureCreateDto lecture) {
+        return lectureFacade.update(id, lecture);
     }
 
     /**
@@ -74,20 +75,21 @@ public class LectureController {
      * @param id the ID of the lecture to delete
      */
     @DeleteMapping("/delete/{id}")
-    public void delete(@PathVariable String id) {
+    public void delete(@PathVariable Long id) {
+        lectureFacade.delete(id);
     }
 
 
     /**
      * Adds lecturer to the existing lecture resource
      *
-     * @param id       id of lecture to update
-     * @param lecturer UserDto for the course lecturer
+     * @param id          id of lecture to update
+     * @param lecturerDto UserDto for the course lecturer
      * @return the LectureDto representing the updated lecture
      */
     @PatchMapping("/setLecturer/{id}")
-    public LectureDto setLecturer(@PathVariable String id, @RequestBody UserDto lecturer) {
-        return new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
+    public LectureDto setLecturer(@PathVariable Long id, @RequestBody UserDto lecturerDto) {
+        return lectureFacade.setLecturer(id, lecturerDto);
     }
 
     /**
@@ -98,10 +100,8 @@ public class LectureController {
      * @return the LectureDto representing the updated lecture
      */
     @PatchMapping("/enrol/{id}")
-    public LectureDto enrol(@PathVariable String id, @RequestBody UserDto student) {
-        var lecture = new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
-        lecture.setStudentIds(new ArrayList<>(List.of(student.getId())));
-        return lecture;
+    public LectureDto enrol(@PathVariable Long id, @RequestBody UserDto student) {
+        return lectureFacade.enrol(id, student);
     }
 
     /**
@@ -112,7 +112,7 @@ public class LectureController {
      * @return the LectureDto representing the updated lecture
      */
     @PatchMapping("/expel/{id}")
-    public LectureDto expel(@PathVariable String id, @RequestBody UserDto student) {
-        return new LectureDto(START_DATETIME, END_DATETIME, TOPIC, 10, "0", "0");
+    public LectureDto expel(@PathVariable Long id, @RequestBody UserDto student) {
+        return lectureFacade.expel(id, student);
     }
 }
\ 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
new file mode 100644
index 00000000..08fe710c
--- /dev/null
+++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureFacade.java
@@ -0,0 +1,78 @@
+package org.fuseri.modulelanguageschool.lecture;
+
+import org.fuseri.model.dto.course.LanguageTypeDto;
+import org.fuseri.model.dto.course.ProficiencyLevelDto;
+import org.fuseri.model.dto.lecture.LectureCreateDto;
+import org.fuseri.model.dto.lecture.LectureDto;
+import org.fuseri.model.dto.user.UserDto;
+import org.fuseri.modulelanguageschool.course.Language;
+import org.fuseri.modulelanguageschool.course.ProficiencyLevel;
+import org.fuseri.modulelanguageschool.user.UserMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class LectureFacade {
+
+    private final LectureService lectureService;
+    private final LectureMapper lectureMapper;
+    private final UserMapper userMapper;
+
+    @Autowired
+    public LectureFacade(LectureService lectureService, LectureMapper lectureMapper, UserMapper userMapper) {
+        this.lectureService = lectureService;
+        this.lectureMapper = lectureMapper;
+        this.userMapper = userMapper;
+    }
+
+    @Transactional
+    public LectureDto create(LectureCreateDto dto) {
+        return lectureMapper.mapToDto(lectureService.save(lectureMapper.mapToLecture(dto)));
+    }
+
+    @Cacheable(cacheNames = "courses", key = "#id")
+    @Transactional(readOnly = true)
+    public LectureDto findById(Long id) {
+        return lectureMapper.mapToDto(lectureService.findById(id));
+    }
+
+    @Transactional(readOnly = true)
+    public List<LectureDto> findAll(Long id) {
+        return lectureMapper.mapToList(lectureService.findAllByCourse(id));
+    }
+
+    @Transactional
+    public LectureDto update(Long id, LectureCreateDto dto) {
+        return lectureMapper.mapToDto(lectureService.update(id, lectureMapper.mapToLecture(dto)));
+    }
+
+    @Transactional
+    public void delete(Long id) {
+        lectureService.delete(id);
+    }
+
+    public List<LectureDto> findAll(LanguageTypeDto lang) {
+        return lectureMapper.mapToList(lectureService.findAll(Language.valueOf(lang.name())));
+    }
+
+    public List<LectureDto> findAll(LanguageTypeDto lang, ProficiencyLevelDto prof) {
+        return lectureMapper.mapToList(lectureService.findAll(Language.valueOf(lang.name()), ProficiencyLevel.valueOf(prof.name())));
+    }
+
+    public LectureDto enrol(Long id, UserDto student) {
+        return lectureMapper.mapToDto(lectureService.enrol(id, userMapper.fromDto(student)));
+    }
+
+    public LectureDto expel(Long id, UserDto student) {
+        return lectureMapper.mapToDto(lectureService.expel(id, userMapper.fromDto(student)));
+    }
+
+    public LectureDto setLecturer(Long id, UserDto lecturerDto) {
+        return lectureMapper.mapToDto(lectureService.setLecturer(id, userMapper.fromDto(lecturerDto)));
+    }
+}
diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureMapper.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureMapper.java
new file mode 100644
index 00000000..8136a3e2
--- /dev/null
+++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureMapper.java
@@ -0,0 +1,25 @@
+package org.fuseri.modulelanguageschool.lecture;
+
+import org.fuseri.model.dto.lecture.LectureCreateDto;
+import org.fuseri.model.dto.lecture.LectureDto;
+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 LectureMapper {
+    LectureDto mapToDto(Lecture lecture);
+
+    Lecture mapToLecture(LectureDto lectureDto);
+
+    List<LectureDto> mapToList(List<Lecture> lectures);
+
+
+    default Page<LectureDto> mapToPageDto(Page<Lecture> lectures) {
+        return new PageImpl<>(mapToList(lectures.getContent()), lectures.getPageable(), lectures.getTotalPages());
+    }
+
+    Lecture mapToLecture(LectureCreateDto dto);
+}
diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureRepository.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureRepository.java
new file mode 100644
index 00000000..1fd35bce
--- /dev/null
+++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureRepository.java
@@ -0,0 +1,21 @@
+package org.fuseri.modulelanguageschool.lecture;
+import org.fuseri.modulelanguageschool.course.Language;
+import org.fuseri.modulelanguageschool.course.ProficiencyLevel;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface LectureRepository  extends JpaRepository<Lecture, Long> {
+
+    @Query("SELECT l FROM Lecture l WHERE l.course.id = ?1")
+    List<Lecture> findAllByCourse(Long id);
+
+    @Query("SELECT l FROM Lecture l WHERE l.course.language = ?1")
+    List<Lecture> findAllByLang(Language language);
+
+    @Query("SELECT l FROM Lecture l WHERE l.course.language = ?1 AND l.course.proficiency = ?2")
+    List<Lecture> findAllByLangProf(Language language, ProficiencyLevel proficiencyLevel);
+}
diff --git a/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureService.java b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureService.java
new file mode 100644
index 00000000..a9e90bcb
--- /dev/null
+++ b/application/module-language-school/src/main/java/org/fuseri/modulelanguageschool/lecture/LectureService.java
@@ -0,0 +1,102 @@
+package org.fuseri.modulelanguageschool.lecture;
+
+import org.fuseri.modulelanguageschool.common.ResourceNotFoundException;
+import org.fuseri.modulelanguageschool.course.Language;
+import org.fuseri.modulelanguageschool.course.ProficiencyLevel;
+import org.fuseri.modulelanguageschool.user.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class LectureService {
+
+    private final LectureRepository lectureRepository;
+
+    @Autowired
+    public LectureService(LectureRepository lectureRepository) {
+        this.lectureRepository = lectureRepository;
+    }
+
+    @Transactional
+    public Lecture save(Lecture lecture) {
+        return lectureRepository.save(lecture);
+    }
+
+    @Transactional(readOnly = true)
+    public Lecture findById(Long id) {
+        return lectureRepository.findById(id)
+                .orElseThrow(() -> new ResourceNotFoundException("Lecture with id: " + id + " was not found."));
+    }
+
+    @Transactional(readOnly = true)
+    public List<Lecture> findAllByCourse(Long id) {
+        return lectureRepository.findAllByCourse(id);
+    }
+
+    @Transactional
+    public Lecture update(Long id, Lecture newLecture) {
+        Optional<Lecture> optionalLecture = lectureRepository.findById(id);
+        if (optionalLecture.isPresent()) {
+            Lecture lecture = optionalLecture.get();
+            lecture.setFrom(newLecture.getFrom());
+            lecture.setTo(newLecture.getTo());
+            lecture.setTopic(newLecture.getTopic());
+            lecture.setCourse(newLecture.getCourse());
+            lecture.setLecturer(newLecture.getLecturer());
+            lecture.setStudents(newLecture.getStudents());
+            return lectureRepository.save(lecture);
+        } else {
+            throw new ResourceNotFoundException("Lecture with id: " + id + " was not found.");
+        }
+    }
+
+    @Transactional
+    public void delete(Long id) {
+        lectureRepository.deleteById(id);
+    }
+
+    public List<Lecture> findAll(Language language) {
+        return lectureRepository.findAllByLang(language);
+    }
+
+    public List<Lecture> findAll(Language language, ProficiencyLevel proficiencyLevel) {
+        return lectureRepository.findAllByLangProf(language, proficiencyLevel);
+    }
+
+    public Lecture enrol(Long id, User student) {
+        Optional<Lecture> optionalLecture = lectureRepository.findById(id);
+        if (optionalLecture.isPresent()) {
+            Lecture lecture = optionalLecture.get();
+            lecture.enrolStudent(student);
+            return lectureRepository.save(lecture);
+        } else {
+            throw new ResourceNotFoundException("Lecture with id: " + id + " was not found.");
+        }
+    }
+
+    public Lecture expel(Long id, User student) {
+        Optional<Lecture> optionalLecture = lectureRepository.findById(id);
+        if (optionalLecture.isPresent()) {
+            Lecture lecture = optionalLecture.get();
+            lecture.expelStudent(student);
+            return lectureRepository.save(lecture);
+        } else {
+            throw new ResourceNotFoundException("Lecture with id: " + id + " was not found.");
+        }
+    }
+
+    public Lecture setLecturer(Long id, User lecturer) {
+        Optional<Lecture> optionalLecture = lectureRepository.findById(id);
+        if (optionalLecture.isPresent()) {
+            Lecture lecture = optionalLecture.get();
+            lecture.setLecturer(lecturer);
+            return lectureRepository.save(lecture);
+        } else {
+            throw new ResourceNotFoundException("Lecture with id: " + id + " was not found.");
+        }
+    }
+}
diff --git a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
index e254fd91..7c287861 100644
--- a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
+++ b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
@@ -38,12 +38,12 @@ public class LectureTest {
             LocalDateTime.now().plusDays(2),
             LocalDateTime.now().plusDays(2).plusHours(2),
             "Learning how to spell deprecated",
-            10, "0");
+            10, 0L);
     private final LectureDto lectureDto = new LectureDto(
             LocalDateTime.now().plusDays(2),
             LocalDateTime.now().plusDays(2).plusHours(2),
             "Learning how to spell deprecated",
-            10, "0", "0");
+            10, 0L, 0L);
 
     @Test
     void createLecture() throws Exception {
@@ -81,7 +81,7 @@ public class LectureTest {
 
     @Test
     void findLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.find(id)).thenReturn(lectureDto);
         mockMvc.perform(get("/lectures/find/" + id))
                 .andExpect(status().isOk())
@@ -96,17 +96,17 @@ public class LectureTest {
 
     @Test
     void findLectureWithoutId() throws Exception {
-        Mockito.when(lectureController.find(ArgumentMatchers.anyString())).thenReturn(lectureDto);
+        Mockito.when(lectureController.find(ArgumentMatchers.anyLong())).thenReturn(lectureDto);
         mockMvc.perform(get("/lectures/find/"))
                 .andExpect(status().is4xxClientError());
     }
 
     @Test
     void findLecturesByCourse() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.findByCourse(id)).thenReturn(new ArrayList<>());
         String response = mockMvc.perform(get("/lectures/findByCourse")
-                        .param("courseId", id))
+                        .param("courseId", String.valueOf(id)))
                 .andExpect(status().isOk())
                 .andReturn().getResponse().getContentAsString();
 
@@ -115,14 +115,14 @@ public class LectureTest {
 
     @Test
     void findLecturesByCourseWithoutParameter() throws Exception {
-        Mockito.when(lectureController.findByCourse(ArgumentMatchers.anyString())).thenReturn(new ArrayList<>());
+        Mockito.when(lectureController.findByCourse(ArgumentMatchers.anyLong())).thenReturn(new ArrayList<>());
         mockMvc.perform(get("/lectures/findByCourse"))
                 .andExpect(status().is4xxClientError());
     }
 
     @Test
     void updateLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.update(ArgumentMatchers.eq(id),
                 ArgumentMatchers.isA(LectureCreateDto.class)))
                 .thenReturn(lectureDto);
@@ -142,7 +142,7 @@ public class LectureTest {
     @Test
     void updateLectureWithoutParameter() throws Exception {
         Mockito.when(lectureController.
-                update(ArgumentMatchers.anyString(), ArgumentMatchers.isA(LectureCreateDto.class)))
+                update(ArgumentMatchers.anyLong(), ArgumentMatchers.isA(LectureCreateDto.class)))
                 .thenReturn(lectureDto);
         mockMvc.perform(put("/lectures/update"))
                 .andExpect(status().is4xxClientError());
@@ -150,7 +150,7 @@ public class LectureTest {
 
     @Test
     void deleteLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.doNothing().when(lectureController).delete(id);
         mockMvc.perform(delete("/lectures/delete/" + id))
                         .andExpect(status().isOk());
@@ -158,14 +158,14 @@ public class LectureTest {
 
     @Test
     void deleteCourseWithoutParameter() throws Exception {
-        Mockito.doNothing().when(lectureController).delete(ArgumentMatchers.anyString());
+        Mockito.doNothing().when(lectureController).delete(ArgumentMatchers.anyLong());
         mockMvc.perform(delete("/lectures/delete/"))
                 .andExpect(status().is4xxClientError());
     }
 
     @Test
     void setLecturerForLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.setLecturer(ArgumentMatchers.eq(id),
                 ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
@@ -186,7 +186,7 @@ public class LectureTest {
 
     @Test
     void setLecturerForLectureWithoutParameters() throws Exception {
-        Mockito.when(lectureController.setLecturer(ArgumentMatchers.anyString(),
+        Mockito.when(lectureController.setLecturer(ArgumentMatchers.anyLong(),
                         ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
         mockMvc.perform(patch("/lectures/setLecturer"))
@@ -195,7 +195,7 @@ public class LectureTest {
 
     @Test
     void enrolLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.enrol(ArgumentMatchers.eq(id),
                 ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
@@ -216,7 +216,7 @@ public class LectureTest {
 
     @Test
     void enrolCourseWithoutUserParameters() throws Exception {
-        Mockito.when(lectureController.enrol(ArgumentMatchers.anyString(),
+        Mockito.when(lectureController.enrol(ArgumentMatchers.anyLong(),
                 ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
         mockMvc.perform(patch("/lectures/enrol"))
@@ -225,7 +225,7 @@ public class LectureTest {
 
     @Test
     void expelLecture() throws Exception {
-        String id = "0";
+        Long id = 0L;
         Mockito.when(lectureController.expel(ArgumentMatchers.eq(id),
                 ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
@@ -246,7 +246,7 @@ public class LectureTest {
 
     @Test
     void expelCourseWithoutUserParameters() throws Exception {
-        Mockito.when(lectureController.expel(ArgumentMatchers.anyString(),
+        Mockito.when(lectureController.expel(ArgumentMatchers.anyLong(),
                 ArgumentMatchers.isA(UserDto.class)))
                 .thenReturn(lectureDto);
         mockMvc.perform(patch("/lectures/expel"))
-- 
GitLab


From c9cb7d2c0981f5970c1a4513b4ccd125108e3356 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <xpokorn8@fi.muni.cz>
Date: Sun, 9 Apr 2023 11:42:25 +0200
Subject: [PATCH 4/4] Adding Open API annotations, polishing api

---
 application/module-language-school/pom.xml      |  7 ++++++-
 .../course/CourseController.java                | 17 ++++++++++++++---
 .../lecture/LectureController.java              | 13 ++++++++++++-
 .../modulelanguageschool/course/CourseTest.java |  6 +++---
 .../lecture/LectureTest.java                    |  6 +++---
 5 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/application/module-language-school/pom.xml b/application/module-language-school/pom.xml
index 76585929..d54d174b 100644
--- a/application/module-language-school/pom.xml
+++ b/application/module-language-school/pom.xml
@@ -63,8 +63,13 @@
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-tx</artifactId>
 		</dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.6.9</version>
+        </dependency>
 
-	</dependencies>
+    </dependencies>
 
 	<build>
 		<plugins>
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 6d11099e..353dcd43 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,7 @@
 package org.fuseri.modulelanguageschool.course;
 
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import jakarta.validation.Valid;
 import org.fuseri.model.dto.course.CourseCreateDto;
 import org.fuseri.model.dto.course.CourseDto;
@@ -12,13 +14,13 @@ import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
  * This class represents a RESTful controller for Courses resources.
  * It handles incoming HTTP requests related to courses, and delegates them to the appropriate service method.
  */
+@Api(tags = "Course Controller")
 @RestController
 @RequestMapping("/courses")
 public class CourseController {
@@ -36,7 +38,8 @@ public class CourseController {
      * @param dto the CourseCreateDto containing the course data
      * @return the newly created CourseDto
      */
-    @PostMapping("/create")
+    @ApiOperation(value = "Create a new course")
+    @PostMapping
     public CourseDto create(@Valid @RequestBody CourseCreateDto dto) {
         return courseFacade.create(dto);
     }
@@ -47,9 +50,10 @@ public class CourseController {
      * @param id the ID of the course to retrieve
      * @return the CourseDto for the specified ID
      */
+    @ApiOperation(value = "Retrieve a course by ID")
     @GetMapping("/find/{id}")
     public CourseDto find(@PathVariable Long id) {
-         return courseFacade.findById(id);
+        return courseFacade.findById(id);
     }
 
     /**
@@ -58,6 +62,7 @@ public class CourseController {
      * @param page the page number to retrieve
      * @return the Result containing the requested page of CourseDtos
      */
+    @ApiOperation(value = "Retrieve a paginated list of courses")
     @GetMapping("/findAll")
     public Page<CourseDto> findAll(@RequestParam int page) {
         return courseFacade.findAll(PageRequest.of(page, 10, Sort.by(Sort.Direction.ASC, "name")));
@@ -69,6 +74,7 @@ public class CourseController {
      * @param lang the language to find courses of
      * @return the Result containing the requested page of CourseDtos
      */
+    @ApiOperation(value = "Retrieve a paginated list of courses of a given language")
     @GetMapping("/findAllByLang")
     public List<CourseDto> findAll(@RequestParam LanguageTypeDto lang) {
         return courseFacade.findAll(lang);
@@ -81,6 +87,7 @@ public class CourseController {
      * @param prof the proficiency of the language
      * @return the Result containing the requested page of CourseDtos
      */
+    @ApiOperation(value = "Retrieve a paginated list of courses of a given language and proficiency")
     @GetMapping("/findAllByLangProf")
     public List<CourseDto> findAll(@RequestParam LanguageTypeDto lang,
                                    @RequestParam ProficiencyLevelDto prof) {
@@ -94,6 +101,7 @@ public class CourseController {
      * @param dto the CourseCreateDto containing the updated course data
      * @return the updated CourseDto
      */
+    @ApiOperation(value = "Update an existing course")
     @PutMapping("/update/{id}")
     public CourseDto update(@PathVariable Long id, @Valid @RequestBody CourseCreateDto dto) {
         return courseFacade.update(id, dto);
@@ -104,6 +112,7 @@ public class CourseController {
      *
      * @param id the ID of the course to delete
      */
+    @ApiOperation(value = "Delete a course by ID")
     @DeleteMapping("/delete/{id}")
     public void delete(@PathVariable Long id) {
         courseFacade.delete(id);
@@ -117,6 +126,7 @@ public class CourseController {
      * @param student UserDto for the student
      * @return the CourseDto representing the updated course
      */
+    @ApiOperation(value = "Add student to the existing course")
     @PatchMapping("/enrol/{id}")
     public CourseDto enrol(@PathVariable Long id, @RequestBody UserDto student) {
         return courseFacade.enrol(id, student);
@@ -129,6 +139,7 @@ public class CourseController {
      * @param student UserDto for the student
      * @return the CourseDto representing the updated course
      */
+    @ApiOperation(value = "Remove student from the existing course")
     @PatchMapping("/expel/{id}")
     public CourseDto expel(@PathVariable Long id, @RequestBody UserDto student) {
         return courseFacade.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 99da62d6..0922a5e2 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,7 @@
 package org.fuseri.modulelanguageschool.lecture;
 
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import jakarta.validation.Valid;
 import org.fuseri.model.dto.lecture.LectureCreateDto;
 import org.fuseri.model.dto.lecture.LectureDto;
@@ -13,6 +15,7 @@ import java.util.List;
  * This class represents a RESTful controller for Lecture resources.
  * It handles incoming HTTP requests related to lectures, and delegates them to the appropriate service method.
  */
+@Api(tags = "Lecture Controller")
 @RestController
 @RequestMapping("/lectures")
 public class LectureController {
@@ -31,7 +34,8 @@ public class LectureController {
      * @param lecture the LectureDto representing the lecture to be created
      * @return the LectureDto representing the newly created lecture
      */
-    @PostMapping("/create")
+    @ApiOperation(value = "Create a new lecture")
+    @PostMapping
     public LectureDto create(@Valid @RequestBody LectureCreateDto lecture) {
         return lectureFacade.create(lecture);
     }
@@ -42,6 +46,7 @@ public class LectureController {
      * @param courseId the ID of the lecture to find
      * @return the LectureDto representing the found lecture
      */
+    @ApiOperation(value = "Retrieve a lecture by its ID")
     @GetMapping("find/{courseId}")
     public LectureDto find(@PathVariable Long courseId) {
         return lectureFacade.findById(courseId);
@@ -53,6 +58,7 @@ public class LectureController {
      * @param courseId the course to retrieve lectures from
      * @return the list of LectureDtos
      */
+    @ApiOperation(value = "Retrieve a list of lectures for the corresponding course")
     @GetMapping("/findByCourse")
     public List<LectureDto> findByCourse(@Valid @RequestParam Long courseId) {
         return lectureFacade.findAll(courseId);
@@ -64,6 +70,7 @@ public class LectureController {
      * @param lecture the CourseCreateDto representing the updated lecture
      * @return the LectureDto representing the updated lecture
      */
+    @ApiOperation(value = "Update an existing lecture")
     @PutMapping("/update/{id}")
     public LectureDto update(@PathVariable Long id, @Valid @RequestBody LectureCreateDto lecture) {
         return lectureFacade.update(id, lecture);
@@ -74,6 +81,7 @@ public class LectureController {
      *
      * @param id the ID of the lecture to delete
      */
+    @ApiOperation(value = "Delete a lecture by its ID")
     @DeleteMapping("/delete/{id}")
     public void delete(@PathVariable Long id) {
         lectureFacade.delete(id);
@@ -87,6 +95,7 @@ public class LectureController {
      * @param lecturerDto UserDto for the course lecturer
      * @return the LectureDto representing the updated lecture
      */
+    @ApiOperation(value = "Add lecturer to the existing lecture")
     @PatchMapping("/setLecturer/{id}")
     public LectureDto setLecturer(@PathVariable Long id, @RequestBody UserDto lecturerDto) {
         return lectureFacade.setLecturer(id, lecturerDto);
@@ -99,6 +108,7 @@ public class LectureController {
      * @param student UserDto for the course student
      * @return the LectureDto representing the updated lecture
      */
+    @ApiOperation(value = "Add student to the existing lecture")
     @PatchMapping("/enrol/{id}")
     public LectureDto enrol(@PathVariable Long id, @RequestBody UserDto student) {
         return lectureFacade.enrol(id, student);
@@ -111,6 +121,7 @@ public class LectureController {
      * @param student UserDto for the course student
      * @return the LectureDto representing the updated lecture
      */
+    @ApiOperation(value = "Remove student from the existing lecture")
     @PatchMapping("/expel/{id}")
     public LectureDto expel(@PathVariable Long id, @RequestBody UserDto student) {
         return lectureFacade.expel(id, student);
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 bab37001..ec5cff89 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
@@ -53,7 +53,7 @@ public class CourseTest {
     @Test
     void createCourse() throws Exception {
         Mockito.when(courseController.create(ArgumentMatchers.isA(CourseCreateDto.class))).thenReturn(courseDto);
-        mockMvc.perform(post("/courses/create")
+        mockMvc.perform(post("/courses")
                         .content(asJsonString(courseCreateDto))
                         .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().isOk())
@@ -70,7 +70,7 @@ public class CourseTest {
         CourseCreateDto invalidCourseCreateDto =
                 new CourseCreateDto(null, null, null, null);
         Mockito.when(courseController.create(ArgumentMatchers.isA(CourseCreateDto.class))).thenReturn(courseDto);
-        mockMvc.perform(post("/courses/create")
+        mockMvc.perform(post("/courses")
                         .content(asJsonString(invalidCourseCreateDto))
                         .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().is4xxClientError());
@@ -79,7 +79,7 @@ public class CourseTest {
     @Test
     void createCourseWithoutParameter() throws Exception {
         Mockito.when(courseController.create(ArgumentMatchers.isA(CourseCreateDto.class))).thenReturn(courseDto);
-        mockMvc.perform(post("/courses/create"))
+        mockMvc.perform(post("/courses"))
                 .andExpect(status().is4xxClientError());
     }
 
diff --git a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
index 7c287861..a0d327ac 100644
--- a/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
+++ b/application/module-language-school/src/test/java/org/fuseri/modulelanguageschool/lecture/LectureTest.java
@@ -48,7 +48,7 @@ public class LectureTest {
     @Test
     void createLecture() throws Exception {
         Mockito.when(lectureController.create(ArgumentMatchers.isA(LectureCreateDto.class))).thenReturn(lectureDto);
-        mockMvc.perform(post("/lectures/create")
+        mockMvc.perform(post("/lectures")
                         .content(asJsonString(lectureCreateDto))
                         .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().isOk())
@@ -66,7 +66,7 @@ public class LectureTest {
         LectureCreateDto invalidLectureCreateDto =
                 new LectureCreateDto(null, null, null, null, null);
         Mockito.when(lectureController.create(ArgumentMatchers.isA(LectureCreateDto.class))).thenReturn(lectureDto);
-        mockMvc.perform(post("/lectures/create")
+        mockMvc.perform(post("/lectures")
                         .content(asJsonString(invalidLectureCreateDto))
                         .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().is4xxClientError());
@@ -75,7 +75,7 @@ public class LectureTest {
     @Test
     void createLectureWithoutParameter() throws Exception {
         Mockito.when(lectureController.create(ArgumentMatchers.isA(LectureCreateDto.class))).thenReturn(lectureDto);
-        mockMvc.perform(post("/lectures/create"))
+        mockMvc.perform(post("/lectures"))
                 .andExpect(status().is4xxClientError());
     }
 
-- 
GitLab