From bc10f5b31b3a8519d1cc28ed2da56e62f7179752 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:25:04 +0000 Subject: [PATCH 1/7] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4a80115..19aec56 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/TvkQWWs6) # Features of modern Java # Цели и задачи л/р: From f537b9e3c1e3832048a6143e6ecd7a11cd63d6f4 Mon Sep 17 00:00:00 2001 From: railolog Date: Fri, 9 Jan 2026 17:40:15 +0800 Subject: [PATCH 2/7] init user creation --- .gitignore | 3 +- build.gradle.kts | 13 +++ src/main/java/org/lab/Application.java | 11 ++ src/main/java/org/lab/Main.java | 4 - .../java/org/lab/config/SwaggerConfig.java | 20 ++++ .../org/lab/controller/UserController.java | 103 ++++++++++++++++++ .../org/lab/dto/UserRegistrationRequest.java | 10 ++ src/main/java/org/lab/model/Role.java | 8 ++ src/main/java/org/lab/model/User.java | 59 ++++++++++ .../org/lab/repository/UserRepository.java | 47 ++++++++ .../java/org/lab/service/UserService.java | 51 +++++++++ 11 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/lab/Application.java delete mode 100644 src/main/java/org/lab/Main.java create mode 100644 src/main/java/org/lab/config/SwaggerConfig.java create mode 100644 src/main/java/org/lab/controller/UserController.java create mode 100644 src/main/java/org/lab/dto/UserRegistrationRequest.java create mode 100644 src/main/java/org/lab/model/Role.java create mode 100644 src/main/java/org/lab/model/User.java create mode 100644 src/main/java/org/lab/repository/UserRepository.java create mode 100644 src/main/java/org/lab/service/UserService.java diff --git a/.gitignore b/.gitignore index b63da45..bf3e1b2 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +/.idea/ diff --git a/build.gradle.kts b/build.gradle.kts index 79bf52a..bc56e76 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,12 +9,25 @@ repositories { mavenCentral() } +java { + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 +} + + dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:4.0.1") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.1") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } +tasks.withType { + options.compilerArgs.add("--enable-preview") + options.compilerArgs.add("-parameters") +} tasks.test { useJUnitPlatform() + jvmArgs("--enable-preview") } \ No newline at end of file diff --git a/src/main/java/org/lab/Application.java b/src/main/java/org/lab/Application.java new file mode 100644 index 0000000..afa57cd --- /dev/null +++ b/src/main/java/org/lab/Application.java @@ -0,0 +1,11 @@ +package org.lab; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java deleted file mode 100644 index 22028ef..0000000 --- a/src/main/java/org/lab/Main.java +++ /dev/null @@ -1,4 +0,0 @@ -void main() { - IO.println("Hello and welcome!"); -} - diff --git a/src/main/java/org/lab/config/SwaggerConfig.java b/src/main/java/org/lab/config/SwaggerConfig.java new file mode 100644 index 0000000..14d88ac --- /dev/null +++ b/src/main/java/org/lab/config/SwaggerConfig.java @@ -0,0 +1,20 @@ +package org.lab.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.Contact; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("User Management API") + .version("1.0.0") + .description("API for user registration and management with modern Java features")); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/controller/UserController.java b/src/main/java/org/lab/controller/UserController.java new file mode 100644 index 0000000..287685a --- /dev/null +++ b/src/main/java/org/lab/controller/UserController.java @@ -0,0 +1,103 @@ +package org.lab.controller; + +import org.lab.dto.UserRegistrationRequest; +import org.lab.model.User; +import org.lab.service.UserService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/users") +public class UserController { + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/register") + public ResponseEntity registerUser( + @RequestBody UserRegistrationRequest request) { + try { + User registeredUser = userService.registerUser( + request.login(), + request.password(), + request.role() + ); + + UserResponse response = new UserResponse( + registeredUser.getId(), + registeredUser.getLogin(), + registeredUser.getRole() + ); + + return ResponseEntity.status(HttpStatus.OK).body(response); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Registration failed")); + } + } + + @GetMapping("/{id}") + public ResponseEntity getUserById( + @PathVariable Long id + ) { + User user = userService.findById(id); + if (user == null) { + return ResponseEntity.notFound().build(); + } + + UserResponse response = new UserResponse( + user.getId(), + user.getLogin(), + user.getRole() + ); + + return ResponseEntity.ok(response); + } + + public static class UserResponse { + private Long id; + private String login; + private org.lab.model.Role role; + + public UserResponse(Long id, String login, org.lab.model.Role role) { + this.id = id; + this.login = login; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public org.lab.model.Role getRole() { + return role; + } + } + + public static class ErrorResponse { + private String message; + + public ErrorResponse(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/dto/UserRegistrationRequest.java b/src/main/java/org/lab/dto/UserRegistrationRequest.java new file mode 100644 index 0000000..e77ee5b --- /dev/null +++ b/src/main/java/org/lab/dto/UserRegistrationRequest.java @@ -0,0 +1,10 @@ +package org.lab.dto; + +import org.lab.model.Role; + +public record UserRegistrationRequest( + String login, + String password, + Role role +) { +} diff --git a/src/main/java/org/lab/model/Role.java b/src/main/java/org/lab/model/Role.java new file mode 100644 index 0000000..11bf2e2 --- /dev/null +++ b/src/main/java/org/lab/model/Role.java @@ -0,0 +1,8 @@ +package org.lab.model; + +public enum Role { + MANAGER, + TEAMLEAD, + DEVELOPER, + QA, +} diff --git a/src/main/java/org/lab/model/User.java b/src/main/java/org/lab/model/User.java new file mode 100644 index 0000000..1f407d5 --- /dev/null +++ b/src/main/java/org/lab/model/User.java @@ -0,0 +1,59 @@ +package org.lab.model; + +public class User { + private Long id; + private Role role; + private String login; + private String password; + + public User() {} + + public User(Long id, Role role, String login, String password) { + this.id = id; + this.role = role; + this.login = login; + this.password = password; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", role=" + role + + ", login='" + login + '\'' + + ", password='[PROTECTED]'" + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/repository/UserRepository.java b/src/main/java/org/lab/repository/UserRepository.java new file mode 100644 index 0000000..bc41564 --- /dev/null +++ b/src/main/java/org/lab/repository/UserRepository.java @@ -0,0 +1,47 @@ +package org.lab.repository; + +import org.lab.model.User; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +@Service +public class UserRepository { + private final Map users = new HashMap<>(); + private final AtomicLong idGenerator = new AtomicLong(1); + + public User save(User user) { + if (user.getId() == null) { + user.setId(idGenerator.getAndIncrement()); + } + users.put(user.getId(), user); + return user; + } + + public Optional findById(Long id) { + return Optional.ofNullable(users.get(id)); + } + + public Optional findByLogin(String login) { + return users.values().stream() + .filter(user -> user.getLogin().equals(login)) + .findFirst(); + } + + public boolean existsByLogin(String login) { + return users.values().stream() + .anyMatch(user -> user.getLogin().equals(login)); + } + + public void deleteById(Long id) { + users.remove(id); + } + + public Map findAll() { + return new HashMap<>(users); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java new file mode 100644 index 0000000..f6c9f52 --- /dev/null +++ b/src/main/java/org/lab/service/UserService.java @@ -0,0 +1,51 @@ +package org.lab.service; + +import org.lab.model.Role; +import org.lab.model.User; +import org.lab.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + + private final UserRepository userRepository; + + @Autowired + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User registerUser(String login, String password, Role role) { + if (userRepository.existsByLogin(login)) { + throw new IllegalArgumentException("User with login '" + login + "' already exists"); + } + + if (login == null || login.trim().isEmpty()) { + throw new IllegalArgumentException("Login cannot be empty"); + } + + if (password == null || password.trim().isEmpty()) { + throw new IllegalArgumentException("Password cannot be empty"); + } + + if (role == null) { + role = Role.USER; // Default role + } + + User user = new User(); + user.setLogin(login.trim()); + user.setPassword(password); // In real application, password should be hashed + user.setRole(role); + + return userRepository.save(user); + } + + public User findByLogin(String login) { + return userRepository.findByLogin(login).orElse(null); + } + + public User findById(Long id) { + return userRepository.findById(id).orElse(null); + } +} \ No newline at end of file From f7b6ffcab7a306b051d50eea85832a558ff0ea1f Mon Sep 17 00:00:00 2001 From: railolog Date: Tue, 13 Jan 2026 09:03:32 +0300 Subject: [PATCH 3/7] create all models --- build.gradle.kts | 3 +- src/main/java/org/lab/Application.java | 11 -- src/main/java/org/lab/Main.java | 3 + .../java/org/lab/config/SwaggerConfig.java | 20 ---- .../org/lab/controller/UserController.java | 103 ------------------ src/main/java/org/lab/model/BugReport.java | 6 + .../java/org/lab/model/BugReportStatus.java | 8 ++ src/main/java/org/lab/model/Milestone.java | 11 ++ .../java/org/lab/model/MilestoneStatus.java | 7 ++ src/main/java/org/lab/model/Project.java | 12 ++ src/main/java/org/lab/model/Ticket.java | 10 ++ src/main/java/org/lab/model/TicketStatus.java | 8 ++ src/main/java/org/lab/model/User.java | 61 ++--------- .../org/lab/repository/UserRepository.java | 21 +--- .../java/org/lab/service/UserService.java | 18 +-- 15 files changed, 81 insertions(+), 221 deletions(-) delete mode 100644 src/main/java/org/lab/Application.java create mode 100644 src/main/java/org/lab/Main.java delete mode 100644 src/main/java/org/lab/config/SwaggerConfig.java delete mode 100644 src/main/java/org/lab/controller/UserController.java create mode 100644 src/main/java/org/lab/model/BugReport.java create mode 100644 src/main/java/org/lab/model/BugReportStatus.java create mode 100644 src/main/java/org/lab/model/Milestone.java create mode 100644 src/main/java/org/lab/model/MilestoneStatus.java create mode 100644 src/main/java/org/lab/model/Project.java create mode 100644 src/main/java/org/lab/model/Ticket.java create mode 100644 src/main/java/org/lab/model/TicketStatus.java diff --git a/build.gradle.kts b/build.gradle.kts index bc56e76..14bc8a5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,8 +16,7 @@ java { dependencies { - implementation("org.springframework.boot:spring-boot-starter-web:4.0.1") - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.1") + implementation("org.projectlombok:lombok:1.18.42") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/src/main/java/org/lab/Application.java b/src/main/java/org/lab/Application.java deleted file mode 100644 index afa57cd..0000000 --- a/src/main/java/org/lab/Application.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.lab; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Application { - static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} \ No newline at end of file diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java new file mode 100644 index 0000000..6cbad3a --- /dev/null +++ b/src/main/java/org/lab/Main.java @@ -0,0 +1,3 @@ +void main() { + IO.println("Hello"); +} \ No newline at end of file diff --git a/src/main/java/org/lab/config/SwaggerConfig.java b/src/main/java/org/lab/config/SwaggerConfig.java deleted file mode 100644 index 14d88ac..0000000 --- a/src/main/java/org/lab/config/SwaggerConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.lab.config; - -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.Contact; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SwaggerConfig { - - @Bean - public OpenAPI customOpenAPI() { - return new OpenAPI() - .info(new Info() - .title("User Management API") - .version("1.0.0") - .description("API for user registration and management with modern Java features")); - } -} \ No newline at end of file diff --git a/src/main/java/org/lab/controller/UserController.java b/src/main/java/org/lab/controller/UserController.java deleted file mode 100644 index 287685a..0000000 --- a/src/main/java/org/lab/controller/UserController.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.lab.controller; - -import org.lab.dto.UserRegistrationRequest; -import org.lab.model.User; -import org.lab.service.UserService; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/users") -public class UserController { - - private final UserService userService; - - public UserController(UserService userService) { - this.userService = userService; - } - - @PostMapping("/register") - public ResponseEntity registerUser( - @RequestBody UserRegistrationRequest request) { - try { - User registeredUser = userService.registerUser( - request.login(), - request.password(), - request.role() - ); - - UserResponse response = new UserResponse( - registeredUser.getId(), - registeredUser.getLogin(), - registeredUser.getRole() - ); - - return ResponseEntity.status(HttpStatus.OK).body(response); - } catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(new ErrorResponse("Registration failed")); - } - } - - @GetMapping("/{id}") - public ResponseEntity getUserById( - @PathVariable Long id - ) { - User user = userService.findById(id); - if (user == null) { - return ResponseEntity.notFound().build(); - } - - UserResponse response = new UserResponse( - user.getId(), - user.getLogin(), - user.getRole() - ); - - return ResponseEntity.ok(response); - } - - public static class UserResponse { - private Long id; - private String login; - private org.lab.model.Role role; - - public UserResponse(Long id, String login, org.lab.model.Role role) { - this.id = id; - this.login = login; - this.role = role; - } - - public Long getId() { - return id; - } - - public String getLogin() { - return login; - } - - public org.lab.model.Role getRole() { - return role; - } - } - - public static class ErrorResponse { - private String message; - - public ErrorResponse(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - } -} \ No newline at end of file diff --git a/src/main/java/org/lab/model/BugReport.java b/src/main/java/org/lab/model/BugReport.java new file mode 100644 index 0000000..8ec947e --- /dev/null +++ b/src/main/java/org/lab/model/BugReport.java @@ -0,0 +1,6 @@ +package org.lab.model; + +public class BugReport { + private long id; + private long projectId; +} diff --git a/src/main/java/org/lab/model/BugReportStatus.java b/src/main/java/org/lab/model/BugReportStatus.java new file mode 100644 index 0000000..3f897c1 --- /dev/null +++ b/src/main/java/org/lab/model/BugReportStatus.java @@ -0,0 +1,8 @@ +package org.lab.model; + +public enum BugReportStatus { + NEW, + FIXED, + TESTED, + CLOSED, +} diff --git a/src/main/java/org/lab/model/Milestone.java b/src/main/java/org/lab/model/Milestone.java new file mode 100644 index 0000000..8b9132c --- /dev/null +++ b/src/main/java/org/lab/model/Milestone.java @@ -0,0 +1,11 @@ +package org.lab.model; + +import java.time.LocalDate; +import java.util.Set; + +public class Milestone { + private MilestoneStatus status; + private LocalDate startDate; + private LocalDate endDate; + private Set tickets; +} diff --git a/src/main/java/org/lab/model/MilestoneStatus.java b/src/main/java/org/lab/model/MilestoneStatus.java new file mode 100644 index 0000000..fe4dae4 --- /dev/null +++ b/src/main/java/org/lab/model/MilestoneStatus.java @@ -0,0 +1,7 @@ +package org.lab.model; + +public enum MilestoneStatus { + OPEN, + ACTIVE, + CLOSED, +} diff --git a/src/main/java/org/lab/model/Project.java b/src/main/java/org/lab/model/Project.java new file mode 100644 index 0000000..8123b51 --- /dev/null +++ b/src/main/java/org/lab/model/Project.java @@ -0,0 +1,12 @@ +package org.lab.model; + +import java.util.Set; + +public class Project { + private long id; + private Set developers; + private Set testers; + private User manager; + private User teamLeader; + private Set milestones; +} diff --git a/src/main/java/org/lab/model/Ticket.java b/src/main/java/org/lab/model/Ticket.java new file mode 100644 index 0000000..ad80407 --- /dev/null +++ b/src/main/java/org/lab/model/Ticket.java @@ -0,0 +1,10 @@ +package org.lab.model; + +import java.util.Set; + +public class Ticket { + private long id; + private long milestoneId; + private TicketStatus status; + private Set assignees; +} diff --git a/src/main/java/org/lab/model/TicketStatus.java b/src/main/java/org/lab/model/TicketStatus.java new file mode 100644 index 0000000..fa60cba --- /dev/null +++ b/src/main/java/org/lab/model/TicketStatus.java @@ -0,0 +1,8 @@ +package org.lab.model; + +public enum TicketStatus { + NEW, + ACCEPTED, + IN_PROGRESS, + COMPLETED, +} diff --git a/src/main/java/org/lab/model/User.java b/src/main/java/org/lab/model/User.java index 1f407d5..dabdffc 100644 --- a/src/main/java/org/lab/model/User.java +++ b/src/main/java/org/lab/model/User.java @@ -1,59 +1,14 @@ package org.lab.model; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@RequiredArgsConstructor public class User { private Long id; - private Role role; private String login; private String password; - - public User() {} - - public User(Long id, Role role, String login, String password) { - this.id = id; - this.role = role; - this.login = login; - this.password = password; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Role getRole() { - return role; - } - - public void setRole(Role role) { - this.role = role; - } - - public String getLogin() { - return login; - } - - public void setLogin(String login) { - this.login = login; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public String toString() { - return "User{" + - "id=" + id + - ", role=" + role + - ", login='" + login + '\'' + - ", password='[PROTECTED]'" + - '}'; - } -} \ No newline at end of file +} diff --git a/src/main/java/org/lab/repository/UserRepository.java b/src/main/java/org/lab/repository/UserRepository.java index bc41564..fc64feb 100644 --- a/src/main/java/org/lab/repository/UserRepository.java +++ b/src/main/java/org/lab/repository/UserRepository.java @@ -1,17 +1,14 @@ package org.lab.repository; -import org.lab.model.User; -import org.springframework.stereotype.Repository; -import org.springframework.stereotype.Service; - -import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -@Service +import org.lab.model.User; + public class UserRepository { - private final Map users = new HashMap<>(); + private final Map users = new ConcurrentHashMap<>(); private final AtomicLong idGenerator = new AtomicLong(1); public User save(User user) { @@ -36,12 +33,4 @@ public boolean existsByLogin(String login) { return users.values().stream() .anyMatch(user -> user.getLogin().equals(login)); } - - public void deleteById(Long id) { - users.remove(id); - } - - public Map findAll() { - return new HashMap<>(users); - } -} \ No newline at end of file +} diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java index f6c9f52..d72b4b5 100644 --- a/src/main/java/org/lab/service/UserService.java +++ b/src/main/java/org/lab/service/UserService.java @@ -1,22 +1,17 @@ package org.lab.service; -import org.lab.model.Role; import org.lab.model.User; import org.lab.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -@Service public class UserService { private final UserRepository userRepository; - @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } - public User registerUser(String login, String password, Role role) { + public User registerUser(String login, String password) { if (userRepository.existsByLogin(login)) { throw new IllegalArgumentException("User with login '" + login + "' already exists"); } @@ -29,22 +24,13 @@ public User registerUser(String login, String password, Role role) { throw new IllegalArgumentException("Password cannot be empty"); } - if (role == null) { - role = Role.USER; // Default role - } - User user = new User(); user.setLogin(login.trim()); - user.setPassword(password); // In real application, password should be hashed - user.setRole(role); + user.setPassword(password); return userRepository.save(user); } - public User findByLogin(String login) { - return userRepository.findByLogin(login).orElse(null); - } - public User findById(Long id) { return userRepository.findById(id).orElse(null); } From 518a9726efdd19490bae8adbe5cac18ae26df46f Mon Sep 17 00:00:00 2001 From: railolog Date: Tue, 13 Jan 2026 09:47:34 +0300 Subject: [PATCH 4/7] create repos and services --- src/main/java/org/lab/model/BugReport.java | 7 +- src/main/java/org/lab/model/Milestone.java | 6 ++ src/main/java/org/lab/model/Project.java | 7 +- src/main/java/org/lab/model/Ticket.java | 7 +- .../lab/repository/BugReportRepository.java | 46 ++++++++ .../lab/repository/MilestoneRepository.java | 46 ++++++++ .../org/lab/repository/ProjectRepository.java | 52 +++++++++ .../org/lab/repository/TicketRepository.java | 53 +++++++++ .../org/lab/service/BugReportService.java | 50 +++++++++ .../org/lab/service/MilestoneService.java | 66 ++++++++++++ .../java/org/lab/service/ProjectService.java | 102 ++++++++++++++++++ .../java/org/lab/service/TicketService.java | 100 +++++++++++++++++ 12 files changed, 539 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/lab/repository/BugReportRepository.java create mode 100644 src/main/java/org/lab/repository/MilestoneRepository.java create mode 100644 src/main/java/org/lab/repository/ProjectRepository.java create mode 100644 src/main/java/org/lab/repository/TicketRepository.java create mode 100644 src/main/java/org/lab/service/BugReportService.java create mode 100644 src/main/java/org/lab/service/MilestoneService.java create mode 100644 src/main/java/org/lab/service/ProjectService.java create mode 100644 src/main/java/org/lab/service/TicketService.java diff --git a/src/main/java/org/lab/model/BugReport.java b/src/main/java/org/lab/model/BugReport.java index 8ec947e..2c74620 100644 --- a/src/main/java/org/lab/model/BugReport.java +++ b/src/main/java/org/lab/model/BugReport.java @@ -1,6 +1,11 @@ package org.lab.model; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class BugReport { - private long id; + private Long id; private long projectId; } diff --git a/src/main/java/org/lab/model/Milestone.java b/src/main/java/org/lab/model/Milestone.java index 8b9132c..c8e7f98 100644 --- a/src/main/java/org/lab/model/Milestone.java +++ b/src/main/java/org/lab/model/Milestone.java @@ -3,7 +3,13 @@ import java.time.LocalDate; import java.util.Set; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class Milestone { + private Long id; private MilestoneStatus status; private LocalDate startDate; private LocalDate endDate; diff --git a/src/main/java/org/lab/model/Project.java b/src/main/java/org/lab/model/Project.java index 8123b51..10db8b2 100644 --- a/src/main/java/org/lab/model/Project.java +++ b/src/main/java/org/lab/model/Project.java @@ -2,8 +2,13 @@ import java.util.Set; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class Project { - private long id; + private Long id; private Set developers; private Set testers; private User manager; diff --git a/src/main/java/org/lab/model/Ticket.java b/src/main/java/org/lab/model/Ticket.java index ad80407..3c6a307 100644 --- a/src/main/java/org/lab/model/Ticket.java +++ b/src/main/java/org/lab/model/Ticket.java @@ -2,8 +2,13 @@ import java.util.Set; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class Ticket { - private long id; + private Long id; private long milestoneId; private TicketStatus status; private Set assignees; diff --git a/src/main/java/org/lab/repository/BugReportRepository.java b/src/main/java/org/lab/repository/BugReportRepository.java new file mode 100644 index 0000000..9b78fac --- /dev/null +++ b/src/main/java/org/lab/repository/BugReportRepository.java @@ -0,0 +1,46 @@ +package org.lab.repository; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.List; +import java.util.stream.Collectors; + +import org.lab.model.BugReport; + +public class BugReportRepository { + private final Map bugReports = new ConcurrentHashMap<>(); + private final AtomicLong idGenerator = new AtomicLong(1); + + public BugReport save(BugReport bugReport) { + if (bugReport.getId() == null) { + bugReport.setId(idGenerator.getAndIncrement()); + } + bugReports.put(bugReport.getId(), bugReport); + return bugReport; + } + + public Optional findById(Long id) { + return Optional.ofNullable(bugReports.get(id)); + } + + public List findByProjectId(Long projectId) { + return bugReports.values().stream() + .filter(bugReport -> Objects.equals(bugReport.getProjectId(), projectId)) + .collect(Collectors.toList()); + } + + public List findAll() { + return bugReports.values().stream().collect(Collectors.toList()); + } + + public boolean existsById(Long id) { + return bugReports.containsKey(id); + } + + public void deleteById(Long id) { + bugReports.remove(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/repository/MilestoneRepository.java b/src/main/java/org/lab/repository/MilestoneRepository.java new file mode 100644 index 0000000..d649d84 --- /dev/null +++ b/src/main/java/org/lab/repository/MilestoneRepository.java @@ -0,0 +1,46 @@ +package org.lab.repository; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.List; +import java.util.stream.Collectors; + +import org.lab.model.Milestone; +import org.lab.model.MilestoneStatus; + +public class MilestoneRepository { + private final Map milestones = new ConcurrentHashMap<>(); + private final AtomicLong idGenerator = new AtomicLong(1); + + public Milestone save(Milestone milestone) { + if (milestone.getId() == null) { + milestone.setId(idGenerator.getAndIncrement()); + } + milestones.put(milestone.getId(), milestone); + return milestone; + } + + public Optional findById(Long id) { + return Optional.ofNullable(milestones.get(id)); + } + + public List findByStatus(MilestoneStatus status) { + return milestones.values().stream() + .filter(milestone -> milestone.getStatus().equals(status)) + .collect(Collectors.toList()); + } + + public List findAll() { + return milestones.values().stream().collect(Collectors.toList()); + } + + public boolean existsById(Long id) { + return milestones.containsKey(id); + } + + public void deleteById(Long id) { + milestones.remove(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/repository/ProjectRepository.java b/src/main/java/org/lab/repository/ProjectRepository.java new file mode 100644 index 0000000..ffb6c02 --- /dev/null +++ b/src/main/java/org/lab/repository/ProjectRepository.java @@ -0,0 +1,52 @@ +package org.lab.repository; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.List; +import java.util.stream.Collectors; + +import org.lab.model.Project; +import org.lab.model.User; + +public class ProjectRepository { + private final Map projects = new ConcurrentHashMap<>(); + private final AtomicLong idGenerator = new AtomicLong(1); + + public Project save(Project project) { + if (project.getId() == null) { + project.setId(idGenerator.getAndIncrement()); + } + projects.put(project.getId(), project); + return project; + } + + public Optional findById(Long id) { + return Optional.ofNullable(projects.get(id)); + } + + public List findByManager(User manager) { + return projects.values().stream() + .filter(project -> project.getManager().equals(manager)) + .collect(Collectors.toList()); + } + + public List findByTeamLeader(User teamLeader) { + return projects.values().stream() + .filter(project -> project.getTeamLeader().equals(teamLeader)) + .collect(Collectors.toList()); + } + + public List findAll() { + return projects.values().stream().collect(Collectors.toList()); + } + + public boolean existsById(Long id) { + return projects.containsKey(id); + } + + public void deleteById(Long id) { + projects.remove(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/repository/TicketRepository.java b/src/main/java/org/lab/repository/TicketRepository.java new file mode 100644 index 0000000..d2290e7 --- /dev/null +++ b/src/main/java/org/lab/repository/TicketRepository.java @@ -0,0 +1,53 @@ +package org.lab.repository; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.List; +import java.util.stream.Collectors; + +import org.lab.model.Ticket; +import org.lab.model.TicketStatus; + +public class TicketRepository { + private final Map tickets = new ConcurrentHashMap<>(); + private final AtomicLong idGenerator = new AtomicLong(1); + + public Ticket save(Ticket ticket) { + if (ticket.getId() == null) { + ticket.setId(idGenerator.getAndIncrement()); + } + tickets.put(ticket.getId(), ticket); + return ticket; + } + + public Optional findById(Long id) { + return Optional.ofNullable(tickets.get(id)); + } + + public List findByMilestoneId(Long milestoneId) { + return tickets.values().stream() + .filter(ticket -> Objects.equals(ticket.getMilestoneId(), milestoneId)) + .collect(Collectors.toList()); + } + + public List findByStatus(TicketStatus status) { + return tickets.values().stream() + .filter(ticket -> ticket.getStatus().equals(status)) + .collect(Collectors.toList()); + } + + public List findAll() { + return tickets.values().stream().collect(Collectors.toList()); + } + + public boolean existsById(Long id) { + return tickets.containsKey(id); + } + + public void deleteById(Long id) { + tickets.remove(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/service/BugReportService.java b/src/main/java/org/lab/service/BugReportService.java new file mode 100644 index 0000000..6964018 --- /dev/null +++ b/src/main/java/org/lab/service/BugReportService.java @@ -0,0 +1,50 @@ +package org.lab.service; + +import org.lab.model.BugReport; +import org.lab.repository.BugReportRepository; +import java.util.List; + +public class BugReportService { + + private final BugReportRepository bugReportRepository; + + public BugReportService(BugReportRepository bugReportRepository) { + this.bugReportRepository = bugReportRepository; + } + + public BugReport createBugReport(Long projectId) { + if (projectId == null) { + throw new IllegalArgumentException("Project ID cannot be null"); + } + + BugReport bugReport = new BugReport(); + bugReport.setProjectId(projectId); + + return bugReportRepository.save(bugReport); + } + + public BugReport findById(Long id) { + return bugReportRepository.findById(id).orElse(null); + } + + public List findByProjectId(Long projectId) { + if (projectId == null) { + throw new IllegalArgumentException("Project ID cannot be null"); + } + return bugReportRepository.findByProjectId(projectId); + } + + public List findAll() { + return bugReportRepository.findAll(); + } + + public void deleteById(Long id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + if (!bugReportRepository.existsById(id)) { + throw new IllegalArgumentException("BugReport with ID '" + id + "' does not exist"); + } + bugReportRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/service/MilestoneService.java b/src/main/java/org/lab/service/MilestoneService.java new file mode 100644 index 0000000..9119214 --- /dev/null +++ b/src/main/java/org/lab/service/MilestoneService.java @@ -0,0 +1,66 @@ +package org.lab.service; + +import org.lab.model.Milestone; +import org.lab.model.MilestoneStatus; +import org.lab.repository.MilestoneRepository; +import java.time.LocalDate; +import java.util.List; + +public class MilestoneService { + + private final MilestoneRepository milestoneRepository; + + public MilestoneService(MilestoneRepository milestoneRepository) { + this.milestoneRepository = milestoneRepository; + } + + public Milestone createMilestone(MilestoneStatus status, LocalDate startDate, LocalDate endDate) { + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); + } + + if (startDate == null) { + throw new IllegalArgumentException("Start date cannot be null"); + } + + if (endDate == null) { + throw new IllegalArgumentException("End date cannot be null"); + } + + if (startDate.isAfter(endDate)) { + throw new IllegalArgumentException("Start date cannot be after end date"); + } + + Milestone milestone = new Milestone(); + milestone.setStatus(status); + milestone.setStartDate(startDate); + milestone.setEndDate(endDate); + + return milestoneRepository.save(milestone); + } + + public Milestone findById(Long id) { + return milestoneRepository.findById(id).orElse(null); + } + + public List findByStatus(MilestoneStatus status) { + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); + } + return milestoneRepository.findByStatus(status); + } + + public List findAll() { + return milestoneRepository.findAll(); + } + + public void deleteById(Long id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + if (!milestoneRepository.existsById(id)) { + throw new IllegalArgumentException("Milestone with ID '" + id + "' does not exist"); + } + milestoneRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/service/ProjectService.java b/src/main/java/org/lab/service/ProjectService.java new file mode 100644 index 0000000..fad189a --- /dev/null +++ b/src/main/java/org/lab/service/ProjectService.java @@ -0,0 +1,102 @@ +package org.lab.service; + +import org.lab.model.Project; +import org.lab.model.User; +import org.lab.repository.ProjectRepository; +import java.util.List; +import java.util.Set; +import java.util.HashSet; + +public class ProjectService { + + private final ProjectRepository projectRepository; + + public ProjectService(ProjectRepository projectRepository) { + this.projectRepository = projectRepository; + } + + public Project createProject(User manager, User teamLeader) { + if (manager == null) { + throw new IllegalArgumentException("Manager cannot be null"); + } + + if (teamLeader == null) { + throw new IllegalArgumentException("Team leader cannot be null"); + } + + Project project = new Project(); + project.setManager(manager); + project.setTeamLeader(teamLeader); + project.setDevelopers(new HashSet<>()); + project.setTesters(new HashSet<>()); + project.setMilestones(new HashSet<>()); + + return projectRepository.save(project); + } + + public Project findById(Long id) { + return projectRepository.findById(id).orElse(null); + } + + public List findByManager(User manager) { + if (manager == null) { + throw new IllegalArgumentException("Manager cannot be null"); + } + return projectRepository.findByManager(manager); + } + + public List findByTeamLeader(User teamLeader) { + if (teamLeader == null) { + throw new IllegalArgumentException("Team leader cannot be null"); + } + return projectRepository.findByTeamLeader(teamLeader); + } + + public List findAll() { + return projectRepository.findAll(); + } + + public Project addDeveloper(Long projectId, User developer) { + if (projectId == null) { + throw new IllegalArgumentException("Project ID cannot be null"); + } + if (developer == null) { + throw new IllegalArgumentException("Developer cannot be null"); + } + + Project project = findById(projectId); + if (project == null) { + throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); + } + + project.getDevelopers().add(developer); + return projectRepository.save(project); + } + + public Project addTester(Long projectId, User tester) { + if (projectId == null) { + throw new IllegalArgumentException("Project ID cannot be null"); + } + if (tester == null) { + throw new IllegalArgumentException("Tester cannot be null"); + } + + Project project = findById(projectId); + if (project == null) { + throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); + } + + project.getTesters().add(tester); + return projectRepository.save(project); + } + + public void deleteById(Long id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + if (!projectRepository.existsById(id)) { + throw new IllegalArgumentException("Project with ID '" + id + "' does not exist"); + } + projectRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/org/lab/service/TicketService.java b/src/main/java/org/lab/service/TicketService.java new file mode 100644 index 0000000..12d517e --- /dev/null +++ b/src/main/java/org/lab/service/TicketService.java @@ -0,0 +1,100 @@ +package org.lab.service; + +import org.lab.model.Ticket; +import org.lab.model.TicketStatus; +import org.lab.model.User; +import org.lab.repository.TicketRepository; +import java.util.List; +import java.util.HashSet; + +public class TicketService { + + private final TicketRepository ticketRepository; + + public TicketService(TicketRepository ticketRepository) { + this.ticketRepository = ticketRepository; + } + + public Ticket createTicket(Long milestoneId, TicketStatus status) { + if (milestoneId == null) { + throw new IllegalArgumentException("Milestone ID cannot be null"); + } + + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); + } + + Ticket ticket = new Ticket(); + ticket.setMilestoneId(milestoneId); + ticket.setStatus(status); + ticket.setAssignees(new HashSet<>()); + + return ticketRepository.save(ticket); + } + + public Ticket findById(Long id) { + return ticketRepository.findById(id).orElse(null); + } + + public List findByMilestoneId(Long milestoneId) { + if (milestoneId == null) { + throw new IllegalArgumentException("Milestone ID cannot be null"); + } + return ticketRepository.findByMilestoneId(milestoneId); + } + + public List findByStatus(TicketStatus status) { + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); + } + return ticketRepository.findByStatus(status); + } + + public List findAll() { + return ticketRepository.findAll(); + } + + public Ticket assignUser(Long ticketId, User user) { + if (ticketId == null) { + throw new IllegalArgumentException("Ticket ID cannot be null"); + } + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + + Ticket ticket = findById(ticketId); + if (ticket == null) { + throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); + } + + ticket.getAssignees().add(user); + return ticketRepository.save(ticket); + } + + public Ticket updateStatus(Long ticketId, TicketStatus status) { + if (ticketId == null) { + throw new IllegalArgumentException("Ticket ID cannot be null"); + } + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); + } + + Ticket ticket = findById(ticketId); + if (ticket == null) { + throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); + } + + ticket.setStatus(status); + return ticketRepository.save(ticket); + } + + public void deleteById(Long id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + if (!ticketRepository.existsById(id)) { + throw new IllegalArgumentException("Ticket with ID '" + id + "' does not exist"); + } + ticketRepository.deleteById(id); + } +} \ No newline at end of file From ff39a5f856bda10e84cfc03c85ba3ca3b3c034b0 Mon Sep 17 00:00:00 2001 From: railolog Date: Tue, 13 Jan 2026 16:57:23 +0300 Subject: [PATCH 5/7] implement methods --- .../org/lab/dto/UserRegistrationRequest.java | 10 -- src/main/java/org/lab/model/BugReport.java | 1 + .../org/lab/service/BugReportService.java | 104 +++++++++++--- .../org/lab/service/MilestoneService.java | 55 +++++++- .../java/org/lab/service/ProjectService.java | 97 ++++++++----- .../java/org/lab/service/TicketService.java | 129 +++++++++++++----- .../service/UserRoleValidationService.java | 42 ++++++ .../java/org/lab/service/UserService.java | 20 ++- 8 files changed, 350 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/org/lab/dto/UserRegistrationRequest.java create mode 100644 src/main/java/org/lab/service/UserRoleValidationService.java diff --git a/src/main/java/org/lab/dto/UserRegistrationRequest.java b/src/main/java/org/lab/dto/UserRegistrationRequest.java deleted file mode 100644 index e77ee5b..0000000 --- a/src/main/java/org/lab/dto/UserRegistrationRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.lab.dto; - -import org.lab.model.Role; - -public record UserRegistrationRequest( - String login, - String password, - Role role -) { -} diff --git a/src/main/java/org/lab/model/BugReport.java b/src/main/java/org/lab/model/BugReport.java index 2c74620..4578b9b 100644 --- a/src/main/java/org/lab/model/BugReport.java +++ b/src/main/java/org/lab/model/BugReport.java @@ -8,4 +8,5 @@ public class BugReport { private Long id; private long projectId; + private BugReportStatus status; } diff --git a/src/main/java/org/lab/service/BugReportService.java b/src/main/java/org/lab/service/BugReportService.java index 6964018..08f881b 100644 --- a/src/main/java/org/lab/service/BugReportService.java +++ b/src/main/java/org/lab/service/BugReportService.java @@ -1,16 +1,23 @@ package org.lab.service; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; import org.lab.model.BugReport; +import org.lab.model.BugReportStatus; +import org.lab.model.Role; +import org.lab.model.User; import org.lab.repository.BugReportRepository; -import java.util.List; +import org.lab.repository.ProjectRepository; +@RequiredArgsConstructor public class BugReportService { - - private final BugReportRepository bugReportRepository; - public BugReportService(BugReportRepository bugReportRepository) { - this.bugReportRepository = bugReportRepository; - } + private final BugReportRepository bugReportRepository; + private final ProjectRepository projectRepository; + private final UserRoleValidationService userRoleValidationService; public BugReport createBugReport(Long projectId) { if (projectId == null) { @@ -27,24 +34,89 @@ public BugReport findById(Long id) { return bugReportRepository.findById(id).orElse(null); } - public List findByProjectId(Long projectId) { + public Set findBugReportsToFix(User user) { + return projectRepository.findAll().stream() + .filter(project -> project.getDevelopers().contains(user)) + .map(project -> bugReportRepository.findByProjectId(project.getId())) + .flatMap(List::stream) + .filter(bugReport -> BugReportStatus.NEW.equals(bugReport.getStatus())) + .collect(Collectors.toSet()); + } + + public BugReport createBugReportForProject(User user, Long projectId) { + userRoleValidationService.validateUserHasRoles(user, projectId, Role.DEVELOPER, Role.QA); + if (projectId == null) { throw new IllegalArgumentException("Project ID cannot be null"); } - return bugReportRepository.findByProjectId(projectId); + + BugReport bugReport = new BugReport(); + bugReport.setProjectId(projectId); + bugReport.setStatus(BugReportStatus.NEW); + + return bugReport; + } + + public BugReport fixBugReport(User developer, Long bugReportId) { + BugReport bugReport = findById(bugReportId); + if (bugReport == null) { + throw new IllegalArgumentException("Bug report with ID '" + bugReportId + "' does not exist"); + } + + userRoleValidationService.validateUserHasRoles(developer, bugReport.getProjectId(), Role.DEVELOPER); + + if (!BugReportStatus.NEW.equals(bugReport.getStatus()) && !BugReportStatus.TESTED.equals(bugReport.getStatus())) { + throw new IllegalStateException("Bug report cannot be fixed in current status: " + bugReport.getStatus()); + } + + bugReport.setStatus(BugReportStatus.FIXED); + return bugReport; } - public List findAll() { - return bugReportRepository.findAll(); + public BugReport testProject(User tester, Long projectId) { + userRoleValidationService.validateUserHasRoles(tester, projectId, Role.QA); + + BugReport bugReport = new BugReport(); + bugReport.setProjectId(projectId); + bugReport.setStatus(BugReportStatus.NEW); + + return bugReport; + } + + public BugReport verifyBugFix(User tester, Long bugReportId, boolean isFixed) { + BugReport bugReport = findById(bugReportId); + if (bugReport == null) { + throw new IllegalArgumentException("Bug report with ID '" + bugReportId + "' does not exist"); + } + + userRoleValidationService.validateUserHasRoles(tester, bugReport.getProjectId(), Role.QA); + + if (!BugReportStatus.FIXED.equals(bugReport.getStatus())) { + throw new IllegalStateException("Bug report must be in FIXED status to verify. Current status: " + bugReport.getStatus()); + } + + if (isFixed) { + bugReport.setStatus(BugReportStatus.TESTED); + } else { + bugReport.setStatus(BugReportStatus.NEW); + } + + return bugReport; } - public void deleteById(Long id) { - if (id == null) { - throw new IllegalArgumentException("ID cannot be null"); + public BugReport closeBugReport(User tester, Long bugReportId) { + BugReport bugReport = findById(bugReportId); + if (bugReport == null) { + throw new IllegalArgumentException("Bug report with ID '" + bugReportId + "' does not exist"); } - if (!bugReportRepository.existsById(id)) { - throw new IllegalArgumentException("BugReport with ID '" + id + "' does not exist"); + + userRoleValidationService.validateUserHasRoles(tester, bugReport.getProjectId(), Role.QA); + + if (!BugReportStatus.TESTED.equals(bugReport.getStatus())) { + throw new IllegalStateException("Bug report must be tested before closing. Current status: " + bugReport.getStatus()); } - bugReportRepository.deleteById(id); + + bugReport.setStatus(BugReportStatus.CLOSED); + return bugReport; } } \ No newline at end of file diff --git a/src/main/java/org/lab/service/MilestoneService.java b/src/main/java/org/lab/service/MilestoneService.java index 9119214..b7ebeb9 100644 --- a/src/main/java/org/lab/service/MilestoneService.java +++ b/src/main/java/org/lab/service/MilestoneService.java @@ -1,18 +1,22 @@ package org.lab.service; +import java.time.LocalDate; +import java.util.List; + +import lombok.RequiredArgsConstructor; import org.lab.model.Milestone; import org.lab.model.MilestoneStatus; +import org.lab.model.Project; +import org.lab.model.Role; +import org.lab.model.User; import org.lab.repository.MilestoneRepository; -import java.time.LocalDate; -import java.util.List; +@RequiredArgsConstructor public class MilestoneService { - - private final MilestoneRepository milestoneRepository; - public MilestoneService(MilestoneRepository milestoneRepository) { - this.milestoneRepository = milestoneRepository; - } + private final MilestoneRepository milestoneRepository; + private final ProjectService projectService; + private final UserRoleValidationService userRoleValidationService; public Milestone createMilestone(MilestoneStatus status, LocalDate startDate, LocalDate endDate) { if (status == null) { @@ -63,4 +67,41 @@ public void deleteById(Long id) { } milestoneRepository.deleteById(id); } + + public Milestone createMilestoneForProject( + User manager, + Long projectId, + MilestoneStatus status, + LocalDate startDate, + LocalDate endDate + ) { + userRoleValidationService.validateUserHasRoles(manager, projectId, Role.MANAGER); + if (startDate.isAfter(endDate)) { + throw new IllegalArgumentException("Start date cannot be after end date"); + } + + Milestone milestone = new Milestone(); + milestone.setStatus(status); + milestone.setStartDate(startDate); + milestone.setEndDate(endDate); + + Milestone savedMilestone = milestoneRepository.save(milestone); + + Project project = projectService.findById(projectId); + project.getMilestones().add(savedMilestone); + + return savedMilestone; + } + + public Milestone changeMilestoneStatus(User manager, Long projectId, Long milestoneId, MilestoneStatus newStatus) { + userRoleValidationService.validateUserHasRoles(manager, projectId, Role.MANAGER); + + Milestone milestone = findById(milestoneId); + if (milestone == null) { + throw new IllegalArgumentException("Milestone with ID '" + milestoneId + "' does not exist"); + } + + milestone.setStatus(newStatus); + return milestone; + } } \ No newline at end of file diff --git a/src/main/java/org/lab/service/ProjectService.java b/src/main/java/org/lab/service/ProjectService.java index fad189a..a78055c 100644 --- a/src/main/java/org/lab/service/ProjectService.java +++ b/src/main/java/org/lab/service/ProjectService.java @@ -1,32 +1,29 @@ package org.lab.service; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; import org.lab.model.Project; +import org.lab.model.Role; import org.lab.model.User; import org.lab.repository.ProjectRepository; -import java.util.List; -import java.util.Set; -import java.util.HashSet; +@RequiredArgsConstructor public class ProjectService { - - private final ProjectRepository projectRepository; - public ProjectService(ProjectRepository projectRepository) { - this.projectRepository = projectRepository; - } + private final ProjectRepository projectRepository; + private final UserRoleValidationService userRoleValidationService; - public Project createProject(User manager, User teamLeader) { + public Project createProject(User manager) { if (manager == null) { throw new IllegalArgumentException("Manager cannot be null"); } - if (teamLeader == null) { - throw new IllegalArgumentException("Team leader cannot be null"); - } - Project project = new Project(); project.setManager(manager); - project.setTeamLeader(teamLeader); project.setDevelopers(new HashSet<>()); project.setTesters(new HashSet<>()); project.setMilestones(new HashSet<>()); @@ -38,17 +35,17 @@ public Project findById(Long id) { return projectRepository.findById(id).orElse(null); } + public Set findInvolvedProjects(User user) { + return projectRepository.findAll().stream() + .filter(project -> isMember(user, project)) + .collect(Collectors.toSet()); + } + public List findByManager(User manager) { - if (manager == null) { - throw new IllegalArgumentException("Manager cannot be null"); - } return projectRepository.findByManager(manager); } public List findByTeamLeader(User teamLeader) { - if (teamLeader == null) { - throw new IllegalArgumentException("Team leader cannot be null"); - } return projectRepository.findByTeamLeader(teamLeader); } @@ -57,13 +54,6 @@ public List findAll() { } public Project addDeveloper(Long projectId, User developer) { - if (projectId == null) { - throw new IllegalArgumentException("Project ID cannot be null"); - } - if (developer == null) { - throw new IllegalArgumentException("Developer cannot be null"); - } - Project project = findById(projectId); if (project == null) { throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); @@ -74,13 +64,6 @@ public Project addDeveloper(Long projectId, User developer) { } public Project addTester(Long projectId, User tester) { - if (projectId == null) { - throw new IllegalArgumentException("Project ID cannot be null"); - } - if (tester == null) { - throw new IllegalArgumentException("Tester cannot be null"); - } - Project project = findById(projectId); if (project == null) { throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); @@ -91,12 +74,52 @@ public Project addTester(Long projectId, User tester) { } public void deleteById(Long id) { - if (id == null) { - throw new IllegalArgumentException("ID cannot be null"); - } if (!projectRepository.existsById(id)) { throw new IllegalArgumentException("Project with ID '" + id + "' does not exist"); } projectRepository.deleteById(id); } + + public Project assignTeamLeader(User manager, Long projectId, User teamLeader) { + Project project = findById(projectId); + if (project == null) { + throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); + } + + userRoleValidationService.validateUserHasRoles(manager, project.getId(), Role.MANAGER); + + project.setTeamLeader(teamLeader); + return project; + } + + public Project addDeveloperToProject(User manager, Long projectId, User developer) { + Project project = findById(projectId); + if (project == null) { + throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); + } + + userRoleValidationService.validateUserHasRoles(manager, project.getId(), Role.MANAGER); + + project.getDevelopers().add(developer); + return project; + } + + public Project addTesterToProject(User manager, Long projectId, User tester) { + Project project = findById(projectId); + if (project == null) { + throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); + } + + userRoleValidationService.validateUserHasRoles(manager, project.getId(), Role.MANAGER); + + project.getTesters().add(tester); + return project; + } + + private boolean isMember(User user, Project project) { + return project.getManager() == user || + project.getTeamLeader() == user || + project.getDevelopers().contains(user) || + project.getTesters().contains(user); + } } \ No newline at end of file diff --git a/src/main/java/org/lab/service/TicketService.java b/src/main/java/org/lab/service/TicketService.java index 12d517e..4faeeeb 100644 --- a/src/main/java/org/lab/service/TicketService.java +++ b/src/main/java/org/lab/service/TicketService.java @@ -1,19 +1,24 @@ package org.lab.service; +import java.util.HashSet; + +import lombok.RequiredArgsConstructor; +import org.lab.model.Milestone; +import org.lab.model.Project; +import org.lab.model.Role; import org.lab.model.Ticket; import org.lab.model.TicketStatus; import org.lab.model.User; import org.lab.repository.TicketRepository; -import java.util.List; -import java.util.HashSet; +@RequiredArgsConstructor public class TicketService { - + private final TicketRepository ticketRepository; + private final ProjectService projectService; + private final MilestoneService milestoneService; + private final UserRoleValidationService userRoleValidationService; - public TicketService(TicketRepository ticketRepository) { - this.ticketRepository = ticketRepository; - } public Ticket createTicket(Long milestoneId, TicketStatus status) { if (milestoneId == null) { @@ -36,65 +41,121 @@ public Ticket findById(Long id) { return ticketRepository.findById(id).orElse(null); } - public List findByMilestoneId(Long milestoneId) { - if (milestoneId == null) { - throw new IllegalArgumentException("Milestone ID cannot be null"); + public Ticket assignUser(Long ticketId, User user) { + Ticket ticket = findById(ticketId); + if (ticket == null) { + throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); } - return ticketRepository.findByMilestoneId(milestoneId); + + ticket.getAssignees().add(user); + return ticket; } - public List findByStatus(TicketStatus status) { - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); + public Ticket updateStatus(Long ticketId, TicketStatus status) { + Ticket ticket = findById(ticketId); + if (ticket == null) { + throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); } - return ticketRepository.findByStatus(status); + + ticket.setStatus(status); + return ticket; } - public List findAll() { - return ticketRepository.findAll(); + private Long getProjectIdFromMilestone(Long milestoneId) { + Milestone milestone = milestoneService.findById(milestoneId); + if (milestone == null) { + throw new IllegalArgumentException("Milestone with ID '" + milestoneId + "' does not exist"); + } + + return projectService.findAll().stream() + .filter(project -> project.getMilestones().contains(milestone)) + .findFirst() + .map(Project::getId) + .orElseThrow(() -> new IllegalArgumentException("Milestone with ID '" + milestoneId + "' does not " + + "exist in any project")); } - public Ticket assignUser(Long ticketId, User user) { - if (ticketId == null) { - throw new IllegalArgumentException("Ticket ID cannot be null"); + public Ticket createTicketForProject(User user, Long milestoneId, TicketStatus status) { + Long projectId = getProjectIdFromMilestone(milestoneId); + userRoleValidationService.validateUserHasRoles(user, projectId, Role.MANAGER, Role.TEAMLEAD); + + if (milestoneId == null) { + throw new IllegalArgumentException("Milestone ID cannot be null"); } - if (user == null) { - throw new IllegalArgumentException("User cannot be null"); + + if (status == null) { + throw new IllegalArgumentException("Status cannot be null"); } + Ticket ticket = new Ticket(); + ticket.setMilestoneId(milestoneId); + ticket.setStatus(status); + ticket.setAssignees(new HashSet<>()); + + Ticket savedTicket = ticketRepository.save(ticket); + + Milestone milestone = milestoneService.findById(milestoneId); + if (milestone.getTickets() == null) { + milestone.setTickets(new HashSet<>()); + } + milestone.getTickets().add(savedTicket); + + return savedTicket; + } + + public Ticket assignDeveloperToTicket(User user, Long ticketId, User developer) { Ticket ticket = findById(ticketId); if (ticket == null) { throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); } - ticket.getAssignees().add(user); + Long projectId = getProjectIdFromMilestone(ticket.getMilestoneId()); + userRoleValidationService.validateUserHasRoles(user, projectId, Role.MANAGER, Role.TEAMLEAD); + + Project project = projectService.findById(projectId); + if (!project.getDevelopers().contains(developer)) { + throw new IllegalArgumentException("User is not a developer in this project"); + } + + ticket.getAssignees().add(developer); return ticketRepository.save(ticket); } - public Ticket updateStatus(Long ticketId, TicketStatus status) { + public boolean checkTicketCompletion(User user, Long ticketId) { if (ticketId == null) { throw new IllegalArgumentException("Ticket ID cannot be null"); } - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); - } Ticket ticket = findById(ticketId); if (ticket == null) { throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); } - ticket.setStatus(status); - return ticketRepository.save(ticket); + Long projectId = getProjectIdFromMilestone(ticket.getMilestoneId()); + userRoleValidationService.validateUserHasRoles(user, projectId, Role.MANAGER, Role.TEAMLEAD); + + return TicketStatus.COMPLETED.equals(ticket.getStatus()); } - public void deleteById(Long id) { - if (id == null) { - throw new IllegalArgumentException("ID cannot be null"); + public Ticket executeTicket(User developer, Long ticketId) { + Ticket ticket = findById(ticketId); + if (ticket == null) { + throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); } - if (!ticketRepository.existsById(id)) { - throw new IllegalArgumentException("Ticket with ID '" + id + "' does not exist"); + + Long projectId = getProjectIdFromMilestone(ticket.getMilestoneId()); + userRoleValidationService.validateUserHasRoles(developer, projectId, Role.DEVELOPER); + + if (!ticket.getAssignees().contains(developer)) { + throw new SecurityException("Developer is not assigned to this ticket"); } - ticketRepository.deleteById(id); + + if (TicketStatus.NEW.equals(ticket.getStatus()) || TicketStatus.ACCEPTED.equals(ticket.getStatus())) { + ticket.setStatus(TicketStatus.IN_PROGRESS); + } else if (TicketStatus.IN_PROGRESS.equals(ticket.getStatus())) { + ticket.setStatus(TicketStatus.COMPLETED); + } + + return ticket; } } \ No newline at end of file diff --git a/src/main/java/org/lab/service/UserRoleValidationService.java b/src/main/java/org/lab/service/UserRoleValidationService.java new file mode 100644 index 0000000..35b8cbf --- /dev/null +++ b/src/main/java/org/lab/service/UserRoleValidationService.java @@ -0,0 +1,42 @@ +package org.lab.service; + +import java.util.Arrays; + +import lombok.RequiredArgsConstructor; +import org.lab.model.Project; +import org.lab.model.Role; +import org.lab.model.User; +import org.lab.repository.ProjectRepository; + +@RequiredArgsConstructor +public class UserRoleValidationService { + private final ProjectRepository projectRepository; + + public void validateUserHasRoles(User user, Long projectId, Role... roles) { + Project project = projectRepository.findById(projectId).orElseThrow(() -> + new IllegalArgumentException("Project with ID '" + projectId + "' does not exist") + ); + boolean anyMatch = Arrays.stream(roles).anyMatch(role -> role.equals(getUserRole(user, project))); + if (!anyMatch) { + throw new SecurityException("User does not have required roles"); + } + } + + private Role getUserRole(User user, Project project) { + if (project.getManager() != null && project.getManager().equals(user)) { + return Role.MANAGER; + } + if (project.getTeamLeader() != null && project.getTeamLeader().equals(user)) { + return Role.TEAMLEAD; + } + if (project.getDevelopers().contains(user)) { + return Role.DEVELOPER; + } + if (project.getTesters().contains(user)) { + return Role.QA; + } + return null; + } + + +} diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java index d72b4b5..567f54b 100644 --- a/src/main/java/org/lab/service/UserService.java +++ b/src/main/java/org/lab/service/UserService.java @@ -1,15 +1,21 @@ package org.lab.service; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; +import org.lab.model.Ticket; import org.lab.model.User; +import org.lab.repository.ProjectRepository; +import org.lab.repository.TicketRepository; import org.lab.repository.UserRepository; +@RequiredArgsConstructor public class UserService { private final UserRepository userRepository; - - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; - } + private final ProjectRepository projectRepository; + private final TicketRepository ticketRepository; public User registerUser(String login, String password) { if (userRepository.existsByLogin(login)) { @@ -31,6 +37,12 @@ public User registerUser(String login, String password) { return userRepository.save(user); } + public Set findTasks(User user) { + return ticketRepository.findAll().stream() + .filter(ticket -> ticket.getAssignees().contains(user)) + .collect(Collectors.toSet()); + } + public User findById(Long id) { return userRepository.findById(id).orElse(null); } From 12ce63aa0a3167a6064afc2d29d5fc746c337bfc Mon Sep 17 00:00:00 2001 From: railolog Date: Tue, 13 Jan 2026 20:19:56 +0300 Subject: [PATCH 6/7] prepare demo --- build.gradle.kts | 2 + src/main/java/org/lab/Main.java | 63 +++++++++++++- src/main/java/org/lab/ManagementSystem.java | 64 ++++++++++++++ .../lab/repository/BugReportRepository.java | 12 --- .../org/lab/repository/ProjectRepository.java | 3 +- .../org/lab/repository/TicketRepository.java | 3 +- .../org/lab/repository/UserRepository.java | 6 -- .../org/lab/service/BugReportService.java | 19 +---- .../org/lab/service/MilestoneService.java | 83 +++++-------------- .../java/org/lab/service/ProjectService.java | 35 -------- .../java/org/lab/service/TicketService.java | 61 +------------- .../java/org/lab/service/UserService.java | 10 --- 12 files changed, 157 insertions(+), 204 deletions(-) create mode 100644 src/main/java/org/lab/ManagementSystem.java diff --git a/build.gradle.kts b/build.gradle.kts index 14bc8a5..147752e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,8 @@ java { dependencies { implementation("org.projectlombok:lombok:1.18.42") + compileOnly("org.projectlombok:lombok:1.18.42") + annotationProcessor("org.projectlombok:lombok:1.18.42") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 6cbad3a..0b2be42 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -1,3 +1,64 @@ +import org.lab.ManagementSystem; +import org.lab.model.BugReport; +import org.lab.model.Milestone; +import org.lab.model.MilestoneStatus; +import org.lab.model.Project; +import org.lab.model.Ticket; +import org.lab.model.User; +import org.lab.service.BugReportService; +import org.lab.service.MilestoneService; +import org.lab.service.ProjectService; +import org.lab.service.TicketService; +import org.lab.service.UserService; + void main() { - IO.println("Hello"); + ManagementSystem system = ManagementSystem.defaults(); + + UserService userService = system.userService(); + ProjectService projectService = system.projectService(); + BugReportService bugReportService = system.bugReportService(); + MilestoneService milestoneService = system.milestoneService(); + TicketService ticketService = system.ticketService(); + + User manager = userService.registerUser("manager", "m"); + User developer1 = userService.registerUser("developer1", "d"); + User developer2 = userService.registerUser("developer2", "d"); + User tester = userService.registerUser("tester", "t"); + User teamLead = userService.registerUser("teamLead", "t"); + + Project project = projectService.createProject(manager); + + projectService.assignTeamLeader(manager, project.getId(), teamLead); + projectService.addDeveloperToProject(manager, project.getId(), developer1); + projectService.addDeveloperToProject(manager, project.getId(), developer2); + projectService.addTesterToProject(manager, project.getId(), tester); + + Milestone milestone = milestoneService.createMilestone( + manager, + LocalDate.now(), + LocalDate.now().plusDays(2), + project + ); + + milestoneService.changeMilestoneStatus(manager, project.getId(), milestone.getId(), MilestoneStatus.ACTIVE); + + Ticket ticket = ticketService.createTicketForProject(teamLead, milestone.getId()); + + ticketService.assignDeveloperToTicket(manager, ticket.getId(), developer2); + + ticketService.executeTicket(developer2, ticket.getId()); + ticketService.executeTicket(developer2, ticket.getId()); + + boolean ticketCompletion = ticketService.checkTicketCompletion(teamLead, ticket.getId()); + + BugReport bugReport = bugReportService.testProject(tester, project.getId()); + + Set fixedBugReports = bugReportService.findBugReportsToFix(developer1).stream() + .map(bug -> bugReportService.fixBugReport(developer1, bug.getId())) + .collect(Collectors.toSet()); + + Stream verifiedReports = fixedBugReports.stream() + .map(report -> bugReportService.verifyBugFix(tester, report.getId(), true)); + + verifiedReports.forEach(report -> bugReportService.closeBugReport(manager, report.getId())); } \ No newline at end of file diff --git a/src/main/java/org/lab/ManagementSystem.java b/src/main/java/org/lab/ManagementSystem.java new file mode 100644 index 0000000..49e475e --- /dev/null +++ b/src/main/java/org/lab/ManagementSystem.java @@ -0,0 +1,64 @@ +package org.lab; + +import org.lab.repository.BugReportRepository; +import org.lab.repository.MilestoneRepository; +import org.lab.repository.ProjectRepository; +import org.lab.repository.TicketRepository; +import org.lab.repository.UserRepository; +import org.lab.service.BugReportService; +import org.lab.service.MilestoneService; +import org.lab.service.ProjectService; +import org.lab.service.TicketService; +import org.lab.service.UserRoleValidationService; +import org.lab.service.UserService; + +public record ManagementSystem( + BugReportService bugReportService, + MilestoneService milestoneService, + ProjectService projectService, + TicketService ticketService, + UserService userService, + UserRoleValidationService userRoleValidationService +) { + public static ManagementSystem defaults() { + BugReportRepository bugReportRepository = new BugReportRepository(); + MilestoneRepository milestoneRepository = new MilestoneRepository(); + ProjectRepository projectRepository = new ProjectRepository(); + TicketRepository ticketRepository = new TicketRepository(); + UserRepository userRepository = new UserRepository(); + + UserRoleValidationService userRoleValidationService = new UserRoleValidationService(projectRepository); + + ProjectService projectService = new ProjectService( + projectRepository, + userRoleValidationService + ); + MilestoneService milestoneService = new MilestoneService( + milestoneRepository, + projectService, + ticketRepository, + userRoleValidationService + ); + return new ManagementSystem( + new BugReportService( + bugReportRepository, + projectRepository, + userRoleValidationService + ), + milestoneService, + projectService, + new TicketService( + ticketRepository, + projectService, + milestoneService, + userRoleValidationService + ), + new UserService( + userRepository, + projectRepository, + ticketRepository + ), + userRoleValidationService + ); + } +} diff --git a/src/main/java/org/lab/repository/BugReportRepository.java b/src/main/java/org/lab/repository/BugReportRepository.java index 9b78fac..55e695e 100644 --- a/src/main/java/org/lab/repository/BugReportRepository.java +++ b/src/main/java/org/lab/repository/BugReportRepository.java @@ -31,16 +31,4 @@ public List findByProjectId(Long projectId) { .filter(bugReport -> Objects.equals(bugReport.getProjectId(), projectId)) .collect(Collectors.toList()); } - - public List findAll() { - return bugReports.values().stream().collect(Collectors.toList()); - } - - public boolean existsById(Long id) { - return bugReports.containsKey(id); - } - - public void deleteById(Long id) { - bugReports.remove(id); - } } \ No newline at end of file diff --git a/src/main/java/org/lab/repository/ProjectRepository.java b/src/main/java/org/lab/repository/ProjectRepository.java index ffb6c02..f0bcb62 100644 --- a/src/main/java/org/lab/repository/ProjectRepository.java +++ b/src/main/java/org/lab/repository/ProjectRepository.java @@ -1,5 +1,6 @@ package org.lab.repository; +import java.util.ArrayList; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -39,7 +40,7 @@ public List findByTeamLeader(User teamLeader) { } public List findAll() { - return projects.values().stream().collect(Collectors.toList()); + return new ArrayList<>(projects.values()); } public boolean existsById(Long id) { diff --git a/src/main/java/org/lab/repository/TicketRepository.java b/src/main/java/org/lab/repository/TicketRepository.java index d2290e7..4d6a875 100644 --- a/src/main/java/org/lab/repository/TicketRepository.java +++ b/src/main/java/org/lab/repository/TicketRepository.java @@ -1,5 +1,6 @@ package org.lab.repository; +import java.util.ArrayList; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -40,7 +41,7 @@ public List findByStatus(TicketStatus status) { } public List findAll() { - return tickets.values().stream().collect(Collectors.toList()); + return new ArrayList<>(tickets.values()); } public boolean existsById(Long id) { diff --git a/src/main/java/org/lab/repository/UserRepository.java b/src/main/java/org/lab/repository/UserRepository.java index fc64feb..c476d27 100644 --- a/src/main/java/org/lab/repository/UserRepository.java +++ b/src/main/java/org/lab/repository/UserRepository.java @@ -23,12 +23,6 @@ public Optional findById(Long id) { return Optional.ofNullable(users.get(id)); } - public Optional findByLogin(String login) { - return users.values().stream() - .filter(user -> user.getLogin().equals(login)) - .findFirst(); - } - public boolean existsByLogin(String login) { return users.values().stream() .anyMatch(user -> user.getLogin().equals(login)); diff --git a/src/main/java/org/lab/service/BugReportService.java b/src/main/java/org/lab/service/BugReportService.java index 08f881b..a48c749 100644 --- a/src/main/java/org/lab/service/BugReportService.java +++ b/src/main/java/org/lab/service/BugReportService.java @@ -19,17 +19,6 @@ public class BugReportService { private final ProjectRepository projectRepository; private final UserRoleValidationService userRoleValidationService; - public BugReport createBugReport(Long projectId) { - if (projectId == null) { - throw new IllegalArgumentException("Project ID cannot be null"); - } - - BugReport bugReport = new BugReport(); - bugReport.setProjectId(projectId); - - return bugReportRepository.save(bugReport); - } - public BugReport findById(Long id) { return bugReportRepository.findById(id).orElse(null); } @@ -76,11 +65,7 @@ public BugReport fixBugReport(User developer, Long bugReportId) { public BugReport testProject(User tester, Long projectId) { userRoleValidationService.validateUserHasRoles(tester, projectId, Role.QA); - BugReport bugReport = new BugReport(); - bugReport.setProjectId(projectId); - bugReport.setStatus(BugReportStatus.NEW); - - return bugReport; + return createBugReportForProject(tester, projectId); } public BugReport verifyBugFix(User tester, Long bugReportId, boolean isFixed) { @@ -110,7 +95,7 @@ public BugReport closeBugReport(User tester, Long bugReportId) { throw new IllegalArgumentException("Bug report with ID '" + bugReportId + "' does not exist"); } - userRoleValidationService.validateUserHasRoles(tester, bugReport.getProjectId(), Role.QA); + userRoleValidationService.validateUserHasRoles(tester, bugReport.getProjectId(), Role.MANAGER); if (!BugReportStatus.TESTED.equals(bugReport.getStatus())) { throw new IllegalStateException("Bug report must be tested before closing. Current status: " + bugReport.getStatus()); diff --git a/src/main/java/org/lab/service/MilestoneService.java b/src/main/java/org/lab/service/MilestoneService.java index b7ebeb9..ea615e9 100644 --- a/src/main/java/org/lab/service/MilestoneService.java +++ b/src/main/java/org/lab/service/MilestoneService.java @@ -1,45 +1,40 @@ package org.lab.service; import java.time.LocalDate; -import java.util.List; +import java.util.Objects; import lombok.RequiredArgsConstructor; import org.lab.model.Milestone; import org.lab.model.MilestoneStatus; import org.lab.model.Project; import org.lab.model.Role; +import org.lab.model.TicketStatus; import org.lab.model.User; import org.lab.repository.MilestoneRepository; +import org.lab.repository.TicketRepository; @RequiredArgsConstructor public class MilestoneService { private final MilestoneRepository milestoneRepository; private final ProjectService projectService; + private final TicketRepository ticketRepository; private final UserRoleValidationService userRoleValidationService; - public Milestone createMilestone(MilestoneStatus status, LocalDate startDate, LocalDate endDate) { - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); - } - - if (startDate == null) { - throw new IllegalArgumentException("Start date cannot be null"); - } - - if (endDate == null) { - throw new IllegalArgumentException("End date cannot be null"); - } + public Milestone createMilestone(User manager, LocalDate startDate, LocalDate endDate, Project project) { + userRoleValidationService.validateUserHasRoles(manager, project.getId(), Role.MANAGER); if (startDate.isAfter(endDate)) { throw new IllegalArgumentException("Start date cannot be after end date"); } Milestone milestone = new Milestone(); - milestone.setStatus(status); + milestone.setStatus(MilestoneStatus.OPEN); milestone.setStartDate(startDate); milestone.setEndDate(endDate); + project.getMilestones().add(milestone); + return milestoneRepository.save(milestone); } @@ -47,61 +42,25 @@ public Milestone findById(Long id) { return milestoneRepository.findById(id).orElse(null); } - public List findByStatus(MilestoneStatus status) { - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); - } - return milestoneRepository.findByStatus(status); - } - - public List findAll() { - return milestoneRepository.findAll(); - } - - public void deleteById(Long id) { - if (id == null) { - throw new IllegalArgumentException("ID cannot be null"); - } - if (!milestoneRepository.existsById(id)) { - throw new IllegalArgumentException("Milestone with ID '" + id + "' does not exist"); - } - milestoneRepository.deleteById(id); - } - - public Milestone createMilestoneForProject( - User manager, - Long projectId, - MilestoneStatus status, - LocalDate startDate, - LocalDate endDate - ) { - userRoleValidationService.validateUserHasRoles(manager, projectId, Role.MANAGER); - if (startDate.isAfter(endDate)) { - throw new IllegalArgumentException("Start date cannot be after end date"); - } - - Milestone milestone = new Milestone(); - milestone.setStatus(status); - milestone.setStartDate(startDate); - milestone.setEndDate(endDate); - - Milestone savedMilestone = milestoneRepository.save(milestone); - - Project project = projectService.findById(projectId); - project.getMilestones().add(savedMilestone); - - return savedMilestone; - } - public Milestone changeMilestoneStatus(User manager, Long projectId, Long milestoneId, MilestoneStatus newStatus) { userRoleValidationService.validateUserHasRoles(manager, projectId, Role.MANAGER); Milestone milestone = findById(milestoneId); - if (milestone == null) { - throw new IllegalArgumentException("Milestone with ID '" + milestoneId + "' does not exist"); + if (newStatus == MilestoneStatus.CLOSED) { + validateTicketsClosed(milestone); } milestone.setStatus(newStatus); return milestone; } + + private void validateTicketsClosed(Milestone milestone) { + milestone.getTickets().stream() + .filter(ticket -> Objects.equals(ticket.getMilestoneId(), milestone.getId())) + .forEach(ticket -> { + if (!ticket.getStatus().equals(TicketStatus.COMPLETED)) { + throw new IllegalArgumentException("Ticket with ID '" + ticket.getId() + "' is not completed"); + } + }); + } } \ No newline at end of file diff --git a/src/main/java/org/lab/service/ProjectService.java b/src/main/java/org/lab/service/ProjectService.java index a78055c..f0062ae 100644 --- a/src/main/java/org/lab/service/ProjectService.java +++ b/src/main/java/org/lab/service/ProjectService.java @@ -41,45 +41,10 @@ public Set findInvolvedProjects(User user) { .collect(Collectors.toSet()); } - public List findByManager(User manager) { - return projectRepository.findByManager(manager); - } - - public List findByTeamLeader(User teamLeader) { - return projectRepository.findByTeamLeader(teamLeader); - } - public List findAll() { return projectRepository.findAll(); } - public Project addDeveloper(Long projectId, User developer) { - Project project = findById(projectId); - if (project == null) { - throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); - } - - project.getDevelopers().add(developer); - return projectRepository.save(project); - } - - public Project addTester(Long projectId, User tester) { - Project project = findById(projectId); - if (project == null) { - throw new IllegalArgumentException("Project with ID '" + projectId + "' does not exist"); - } - - project.getTesters().add(tester); - return projectRepository.save(project); - } - - public void deleteById(Long id) { - if (!projectRepository.existsById(id)) { - throw new IllegalArgumentException("Project with ID '" + id + "' does not exist"); - } - projectRepository.deleteById(id); - } - public Project assignTeamLeader(User manager, Long projectId, User teamLeader) { Project project = findById(projectId); if (project == null) { diff --git a/src/main/java/org/lab/service/TicketService.java b/src/main/java/org/lab/service/TicketService.java index 4faeeeb..0db70ce 100644 --- a/src/main/java/org/lab/service/TicketService.java +++ b/src/main/java/org/lab/service/TicketService.java @@ -19,48 +19,10 @@ public class TicketService { private final MilestoneService milestoneService; private final UserRoleValidationService userRoleValidationService; - - public Ticket createTicket(Long milestoneId, TicketStatus status) { - if (milestoneId == null) { - throw new IllegalArgumentException("Milestone ID cannot be null"); - } - - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); - } - - Ticket ticket = new Ticket(); - ticket.setMilestoneId(milestoneId); - ticket.setStatus(status); - ticket.setAssignees(new HashSet<>()); - - return ticketRepository.save(ticket); - } - public Ticket findById(Long id) { return ticketRepository.findById(id).orElse(null); } - public Ticket assignUser(Long ticketId, User user) { - Ticket ticket = findById(ticketId); - if (ticket == null) { - throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); - } - - ticket.getAssignees().add(user); - return ticket; - } - - public Ticket updateStatus(Long ticketId, TicketStatus status) { - Ticket ticket = findById(ticketId); - if (ticket == null) { - throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); - } - - ticket.setStatus(status); - return ticket; - } - private Long getProjectIdFromMilestone(Long milestoneId) { Milestone milestone = milestoneService.findById(milestoneId); if (milestone == null) { @@ -75,21 +37,13 @@ private Long getProjectIdFromMilestone(Long milestoneId) { "exist in any project")); } - public Ticket createTicketForProject(User user, Long milestoneId, TicketStatus status) { + public Ticket createTicketForProject(User user, Long milestoneId) { Long projectId = getProjectIdFromMilestone(milestoneId); userRoleValidationService.validateUserHasRoles(user, projectId, Role.MANAGER, Role.TEAMLEAD); - if (milestoneId == null) { - throw new IllegalArgumentException("Milestone ID cannot be null"); - } - - if (status == null) { - throw new IllegalArgumentException("Status cannot be null"); - } - Ticket ticket = new Ticket(); ticket.setMilestoneId(milestoneId); - ticket.setStatus(status); + ticket.setStatus(TicketStatus.NEW); ticket.setAssignees(new HashSet<>()); Ticket savedTicket = ticketRepository.save(ticket); @@ -122,14 +76,7 @@ public Ticket assignDeveloperToTicket(User user, Long ticketId, User developer) } public boolean checkTicketCompletion(User user, Long ticketId) { - if (ticketId == null) { - throw new IllegalArgumentException("Ticket ID cannot be null"); - } - Ticket ticket = findById(ticketId); - if (ticket == null) { - throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); - } Long projectId = getProjectIdFromMilestone(ticket.getMilestoneId()); userRoleValidationService.validateUserHasRoles(user, projectId, Role.MANAGER, Role.TEAMLEAD); @@ -139,10 +86,6 @@ public boolean checkTicketCompletion(User user, Long ticketId) { public Ticket executeTicket(User developer, Long ticketId) { Ticket ticket = findById(ticketId); - if (ticket == null) { - throw new IllegalArgumentException("Ticket with ID '" + ticketId + "' does not exist"); - } - Long projectId = getProjectIdFromMilestone(ticket.getMilestoneId()); userRoleValidationService.validateUserHasRoles(developer, projectId, Role.DEVELOPER); diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java index 567f54b..5093ee2 100644 --- a/src/main/java/org/lab/service/UserService.java +++ b/src/main/java/org/lab/service/UserService.java @@ -1,10 +1,6 @@ package org.lab.service; -import java.util.Set; -import java.util.stream.Collectors; - import lombok.RequiredArgsConstructor; -import org.lab.model.Ticket; import org.lab.model.User; import org.lab.repository.ProjectRepository; import org.lab.repository.TicketRepository; @@ -37,12 +33,6 @@ public User registerUser(String login, String password) { return userRepository.save(user); } - public Set findTasks(User user) { - return ticketRepository.findAll().stream() - .filter(ticket -> ticket.getAssignees().contains(user)) - .collect(Collectors.toSet()); - } - public User findById(Long id) { return userRepository.findById(id).orElse(null); } From 9a233f9229c14f6d570b10c7cb88cc98d8d7fe88 Mon Sep 17 00:00:00 2001 From: railolog Date: Tue, 13 Jan 2026 21:25:32 +0300 Subject: [PATCH 7/7] polish --- README.md | 13 ++++---- src/main/java/org/lab/Main.java | 33 ++++++++++++++++++- src/main/java/org/lab/model/BugReport.java | 4 ++- src/main/java/org/lab/model/Milestone.java | 2 ++ src/main/java/org/lab/model/Project.java | 2 ++ src/main/java/org/lab/model/Task.java | 5 +++ src/main/java/org/lab/model/Ticket.java | 4 ++- src/main/java/org/lab/model/User.java | 2 ++ .../org/lab/service/BugReportService.java | 26 +++++++++++---- .../java/org/lab/service/TicketService.java | 11 +++++++ .../service/UserRoleValidationService.java | 2 +- 11 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 src/main/java/org/lab/model/Task.java diff --git a/README.md b/README.md index 19aec56..aeb9b42 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ На основе индивидуального задания произвести разработку бизнес-логики бэкэнда entriprise-системы. В ходе реализации необходимо использовать возможности современных версий языка Java: -* Pattern matching для switch -* строковые шаблоны)))))))))))))) -* расширенные возможности стандартной библиотеки Java -* sealed классы и record -* программирование в функциональном стиле -* preview как project Valhalla, structured concurrency... -* и т.д. +* Pattern matching для switch - TicketService.getTaskDescription() +* sealed классы - Task, Ticket, BugReport +* record - ManagementSystem +* программирование в функциональном стиле - Stream API и лямбды +* preview как structured concurrency - BugReportService.findBugReportsToFix() +* compact source files # Обязательное условие: * Использование системы сборки Gradle diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 0b2be42..eee9de5 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -21,17 +21,34 @@ void main() { TicketService ticketService = system.ticketService(); User manager = userService.registerUser("manager", "m"); + System.out.println("Registered manager: " + manager); + User developer1 = userService.registerUser("developer1", "d"); + System.out.println("Registered developer1: " + developer1); + User developer2 = userService.registerUser("developer2", "d"); + System.out.println("Registered developer2: " + developer2); + User tester = userService.registerUser("tester", "t"); + System.out.println("Registered tester: " + tester); + User teamLead = userService.registerUser("teamLead", "t"); + System.out.println("Registered teamLead: " + teamLead); Project project = projectService.createProject(manager); + System.out.println("Created project: " + project); projectService.assignTeamLeader(manager, project.getId(), teamLead); + System.out.println("Assigned team leader to project"); + projectService.addDeveloperToProject(manager, project.getId(), developer1); + System.out.println("Added developer1 to project"); + projectService.addDeveloperToProject(manager, project.getId(), developer2); + System.out.println("Added developer2 to project"); + projectService.addTesterToProject(manager, project.getId(), tester); + System.out.println("Added tester to project"); Milestone milestone = milestoneService.createMilestone( manager, @@ -39,26 +56,40 @@ void main() { LocalDate.now().plusDays(2), project ); + System.out.println("Created milestone: " + milestone); milestoneService.changeMilestoneStatus(manager, project.getId(), milestone.getId(), MilestoneStatus.ACTIVE); + System.out.println("Changed milestone status to ACTIVE " + milestone); Ticket ticket = ticketService.createTicketForProject(teamLead, milestone.getId()); + System.out.println("Created ticket: " + ticket); ticketService.assignDeveloperToTicket(manager, ticket.getId(), developer2); + System.out.println("Assigned developer2 to ticket"); ticketService.executeTicket(developer2, ticket.getId()); + System.out.println("Executed ticket (first time)"); + ticketService.executeTicket(developer2, ticket.getId()); + System.out.println("Executed ticket (second time)"); boolean ticketCompletion = ticketService.checkTicketCompletion(teamLead, ticket.getId()); + System.out.println("Ticket completion status: " + ticketCompletion); BugReport bugReport = bugReportService.testProject(tester, project.getId()); + System.out.println("Created bug report from testing: " + bugReport); Set fixedBugReports = bugReportService.findBugReportsToFix(developer1).stream() .map(bug -> bugReportService.fixBugReport(developer1, bug.getId())) .collect(Collectors.toSet()); + System.out.println("Fixed bug reports: " + fixedBugReports); Stream verifiedReports = fixedBugReports.stream() .map(report -> bugReportService.verifyBugFix(tester, report.getId(), true)); + System.out.println("Verifying bug reports..."); - verifiedReports.forEach(report -> bugReportService.closeBugReport(manager, report.getId())); + verifiedReports.forEach(report -> { + bugReportService.closeBugReport(manager, report.getId()); + System.out.println("Closed bug report: " + report); + }); } \ No newline at end of file diff --git a/src/main/java/org/lab/model/BugReport.java b/src/main/java/org/lab/model/BugReport.java index 4578b9b..7db215e 100644 --- a/src/main/java/org/lab/model/BugReport.java +++ b/src/main/java/org/lab/model/BugReport.java @@ -2,10 +2,12 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter -public class BugReport { +@ToString +public final class BugReport implements Task { private Long id; private long projectId; private BugReportStatus status; diff --git a/src/main/java/org/lab/model/Milestone.java b/src/main/java/org/lab/model/Milestone.java index c8e7f98..65cc66a 100644 --- a/src/main/java/org/lab/model/Milestone.java +++ b/src/main/java/org/lab/model/Milestone.java @@ -5,9 +5,11 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter +@ToString public class Milestone { private Long id; private MilestoneStatus status; diff --git a/src/main/java/org/lab/model/Project.java b/src/main/java/org/lab/model/Project.java index 10db8b2..d500e60 100644 --- a/src/main/java/org/lab/model/Project.java +++ b/src/main/java/org/lab/model/Project.java @@ -4,9 +4,11 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter +@ToString public class Project { private Long id; private Set developers; diff --git a/src/main/java/org/lab/model/Task.java b/src/main/java/org/lab/model/Task.java new file mode 100644 index 0000000..2df2670 --- /dev/null +++ b/src/main/java/org/lab/model/Task.java @@ -0,0 +1,5 @@ +package org.lab.model; + +public sealed interface Task permits Ticket, BugReport { + Long getId(); +} diff --git a/src/main/java/org/lab/model/Ticket.java b/src/main/java/org/lab/model/Ticket.java index 3c6a307..c1830f6 100644 --- a/src/main/java/org/lab/model/Ticket.java +++ b/src/main/java/org/lab/model/Ticket.java @@ -4,10 +4,12 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter -public class Ticket { +@ToString +public final class Ticket implements Task { private Long id; private long milestoneId; private TicketStatus status; diff --git a/src/main/java/org/lab/model/User.java b/src/main/java/org/lab/model/User.java index dabdffc..42c7843 100644 --- a/src/main/java/org/lab/model/User.java +++ b/src/main/java/org/lab/model/User.java @@ -3,9 +3,11 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.ToString; @Getter @Setter +@ToString @RequiredArgsConstructor public class User { private Long id; diff --git a/src/main/java/org/lab/service/BugReportService.java b/src/main/java/org/lab/service/BugReportService.java index a48c749..3523a1a 100644 --- a/src/main/java/org/lab/service/BugReportService.java +++ b/src/main/java/org/lab/service/BugReportService.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Set; +import java.util.concurrent.StructuredTaskScope; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -24,12 +25,24 @@ public BugReport findById(Long id) { } public Set findBugReportsToFix(User user) { - return projectRepository.findAll().stream() - .filter(project -> project.getDevelopers().contains(user)) - .map(project -> bugReportRepository.findByProjectId(project.getId())) - .flatMap(List::stream) - .filter(bugReport -> BugReportStatus.NEW.equals(bugReport.getStatus())) - .collect(Collectors.toSet()); + try (var scope = StructuredTaskScope.open()) { + List>> tasks = projectRepository.findAll().stream() + .filter(project -> project.getDevelopers().contains(user)) + .map(project -> scope.fork(() -> + bugReportRepository.findByProjectId(project.getId()))) + .toList(); + + scope.join(); + + return tasks.stream() + .map(StructuredTaskScope.Subtask::get) + .flatMap(List::stream) + .filter(bugReport -> BugReportStatus.NEW.equals(bugReport.getStatus())) + .collect(Collectors.toSet()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Bug search interrupted", e); + } } public BugReport createBugReportForProject(User user, Long projectId) { @@ -43,6 +56,7 @@ public BugReport createBugReportForProject(User user, Long projectId) { bugReport.setProjectId(projectId); bugReport.setStatus(BugReportStatus.NEW); + bugReportRepository.save(bugReport); return bugReport; } diff --git a/src/main/java/org/lab/service/TicketService.java b/src/main/java/org/lab/service/TicketService.java index 0db70ce..6607f64 100644 --- a/src/main/java/org/lab/service/TicketService.java +++ b/src/main/java/org/lab/service/TicketService.java @@ -3,9 +3,11 @@ import java.util.HashSet; import lombok.RequiredArgsConstructor; +import org.lab.model.BugReport; import org.lab.model.Milestone; import org.lab.model.Project; import org.lab.model.Role; +import org.lab.model.Task; import org.lab.model.Ticket; import org.lab.model.TicketStatus; import org.lab.model.User; @@ -23,6 +25,15 @@ public Ticket findById(Long id) { return ticketRepository.findById(id).orElse(null); } + public String getTaskDescription(Task task) { + return switch (task) { + case BugReport bug -> "Bug Report #" + bug.getId() + " - Status: " + bug.getStatus(); + case Ticket ticket -> "Ticket #" + ticket.getId() + " - Status: " + ticket.getStatus() + + " - Assignees: " + ticket.getAssignees().size(); + }; + } + + private Long getProjectIdFromMilestone(Long milestoneId) { Milestone milestone = milestoneService.findById(milestoneId); if (milestone == null) { diff --git a/src/main/java/org/lab/service/UserRoleValidationService.java b/src/main/java/org/lab/service/UserRoleValidationService.java index 35b8cbf..f55856c 100644 --- a/src/main/java/org/lab/service/UserRoleValidationService.java +++ b/src/main/java/org/lab/service/UserRoleValidationService.java @@ -22,7 +22,7 @@ public void validateUserHasRoles(User user, Long projectId, Role... roles) { } } - private Role getUserRole(User user, Project project) { + public Role getUserRole(User user, Project project) { if (project.getManager() != null && project.getManager().equals(user)) { return Role.MANAGER; }