diff --git a/build.gradle.kts b/build.gradle.kts index 79bf52a..429fefa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,12 @@ plugins { id("java") } +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) + } +} + group = "org.lab" version = "1.0-SNAPSHOT" diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 22028ef..2b7f2bc 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -1,4 +1,148 @@ -void main() { - IO.println("Hello and welcome!"); -} +package org.lab; + +import java.time.LocalDate; +import java.util.stream.Collectors; + +import org.lab.model.BugReport.BugReportRole; +import org.lab.model.BugReport.BugReportStatus; +import org.lab.model.MilestoneModel.MilestoneStatus; +import org.lab.model.ProjectModel.ProjectRole; +import org.lab.model.TaskModel.TaskRole; +import org.lab.model.TaskModel.TaskStatus; +import org.lab.repositories.BugReportRepo.InMemoryBugReportRepo; +import org.lab.repositories.MilestoneRepo.InMemoryMilestoneRepo; +import org.lab.repositories.ProjectRepo.InMemoryProjectRepo; +import org.lab.repositories.TaskRepo.InMemoryTaskRepo; +import org.lab.repositories.UserRepo.InMemoryUserRepo; +import org.lab.services.BugReportService; +import org.lab.services.MilestoneService; +import org.lab.services.ProjectService; +import org.lab.services.TaskService; +import org.lab.services.UserService; + +public class Main { + void main() { + var userRepo = new InMemoryUserRepo(); + var projectRepo = new InMemoryProjectRepo(); + var milestoneRepo = new InMemoryMilestoneRepo(); + var taskRepo = new InMemoryTaskRepo(); + var bugReportRepo = new InMemoryBugReportRepo(); + + var userService = new UserService(userRepo, projectRepo, taskRepo, bugReportRepo); + var projectService = new ProjectService(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + var milestoneService = new MilestoneService(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + var taskService = new TaskService(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + var bugReportService = new BugReportService(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + + var aliceManagerId = userService.registerUser("Alice Manager", "alice@example.com"); + var bobDevId = userService.registerUser("Bob Dev", "bob@example.com"); + var grokTesterId = userService.registerUser("Grok Tester", "grok@example.com"); + var zigLeadId = userService.registerUser("Zig Lead", "zig@example.com"); + + var projectId = projectService.createProject("Project", aliceManagerId); + projectService.updateLead(aliceManagerId, projectId, zigLeadId); + projectService.addMember(aliceManagerId, projectId, bobDevId, ProjectRole.DEV); + projectService.addMember(aliceManagerId, projectId, grokTesterId, ProjectRole.TESTER); + + projectRepo.getProjectById(projectId).ifPresent((project) -> { + IO.println("Project " + project.getId() + " team"); + project.getMembers().stream().forEach((member) -> { + IO.println(member.user().name() + " " + member.role().name()); + }); + IO.println(); + }); + + var milestoneStart = LocalDate.of(2025, 10, 1); + var milestoneEnd = LocalDate.of(2025, 11, 1); + var milestoneId = milestoneService.createMilestone(aliceManagerId, projectId, "MVP Release", milestoneStart, + milestoneEnd); + milestoneRepo.getMilestoneById(milestoneId).ifPresent((milestone) -> { + IO.println("Milestone " + milestoneId + " data"); + IO.println(milestone.getName() + " " + milestone.getStatus().name()); + IO.println(); + }); + milestoneService.updateMilestoneStatus(aliceManagerId, milestoneId, MilestoneStatus.ACTIVE); + milestoneRepo.getMilestoneById(milestoneId).ifPresent((milestone) -> { + IO.println("Milestone " + milestoneId + " data"); + IO.println(milestone.getName() + " " + milestone.getStatus().name()); + IO.println(); + }); + + var taskId = taskService.createTask(aliceManagerId, milestoneId, "task", "task desc"); + taskRepo.getTaskById(taskId).ifPresent((task) -> { + IO.println("Task " + taskId + " data"); + IO.println(task.getName() + " " + task.getDescription() + " " + task.getStatus().name()); + IO.println(); + }); + + var taskStatus = taskService.getTaskStatus(taskId); + IO.println("Task status " + taskStatus.name()); + IO.println(); + + taskRepo.getTaskById(taskId).ifPresent((task) -> { + IO.println("Task " + taskId + " members"); + task.getTicketMembers().entrySet().stream().forEach((memberEntry) -> { + var membersString = memberEntry.getValue().stream().map((member) -> member.user().name()) + .collect(Collectors.joining(", ")); + IO.print("Role " + memberEntry.getKey().name() + ": " + membersString); + }); + IO.println(); + }); + taskService.addMemberToTask(zigLeadId, taskId, bobDevId, TaskRole.DEV); + taskRepo.getTaskById(taskId).ifPresent((task) -> { + IO.println("Task " + taskId + " members"); + task.getTicketMembers().entrySet().stream().forEach((memberEntry) -> { + var membersString = memberEntry.getValue().stream().map((member) -> member.user().name()) + .collect(Collectors.joining(", ")); + IO.print("Role " + memberEntry.getKey().name() + ": " + membersString); + }); + IO.println(); + }); + + taskService.updateTaskStatus(bobDevId, taskId, TaskStatus.IN_PROGESS); + taskRepo.getTaskById(taskId).ifPresent((task) -> { + IO.println("Task " + taskId + " data"); + IO.println(task.getName() + " " + task.getDescription() + " " + task.getStatus().name()); + IO.println(); + }); + + var reportId = bugReportService.createBugReport(grokTesterId, projectId, "report", "report desc"); + bugReportRepo.getBugReportById(reportId).ifPresent((report) -> { + IO.println("Report " + reportId + " data"); + IO.println(report.getName() + " " + report.getDescription() + " " + report.getStatus().name()); + IO.println(); + }); + + var bugReportStatus = bugReportService.getBugReportStatus(reportId); + IO.println("Report status " + bugReportStatus.name()); + IO.println(); + + bugReportRepo.getBugReportById(reportId).ifPresent((report) -> { + IO.println("Report " + reportId + " members"); + report.getTicketMembers().entrySet().stream().forEach((memberEntry) -> { + var membersString = memberEntry.getValue().stream().map((member) -> member.user().name()) + .collect(Collectors.joining(", ")); + IO.print("Role " + memberEntry.getKey().name() + ": " + membersString); + }); + IO.println(); + }); + bugReportService.addMemberToBugReport(zigLeadId, reportId, bobDevId, BugReportRole.DEV); + bugReportRepo.getBugReportById(reportId).ifPresent((task) -> { + IO.println("Report " + reportId + " members"); + task.getTicketMembers().entrySet().stream().forEach((memberEntry) -> { + var membersString = memberEntry.getValue().stream().map((member) -> member.user().name()) + .collect(Collectors.joining(", ")); + IO.print("Role " + memberEntry.getKey().name() + ": " + membersString); + }); + IO.println(); + }); + + bugReportService.updateBugReportStatus(bobDevId, reportId, BugReportStatus.FIXED); + bugReportRepo.getBugReportById(reportId).ifPresent((task) -> { + IO.println("Report " + reportId + " data"); + IO.println(task.getName() + " " + task.getDescription() + " " + task.getStatus().name()); + IO.println(); + }); + } +} diff --git a/src/main/java/org/lab/dto/BugReportDTO.java b/src/main/java/org/lab/dto/BugReportDTO.java new file mode 100644 index 0000000..0ef68f1 --- /dev/null +++ b/src/main/java/org/lab/dto/BugReportDTO.java @@ -0,0 +1,6 @@ +package org.lab.dto; + +import org.lab.model.ProjectModel.Project; + +public record BugReportDTO(Project project, String name, String description) { +} diff --git a/src/main/java/org/lab/dto/MilestoneDTO.java b/src/main/java/org/lab/dto/MilestoneDTO.java new file mode 100644 index 0000000..1ab6b73 --- /dev/null +++ b/src/main/java/org/lab/dto/MilestoneDTO.java @@ -0,0 +1,8 @@ +package org.lab.dto; + +import java.time.LocalDate; + +import org.lab.model.ProjectModel.Project; + +public record MilestoneDTO(Project project, String name, LocalDate start, LocalDate end) { +} \ No newline at end of file diff --git a/src/main/java/org/lab/dto/ProjectDTO.java b/src/main/java/org/lab/dto/ProjectDTO.java new file mode 100644 index 0000000..c9cd52d --- /dev/null +++ b/src/main/java/org/lab/dto/ProjectDTO.java @@ -0,0 +1,6 @@ +package org.lab.dto; + +import org.lab.model.ProjectModel.ProjectMember; + +public record ProjectDTO(String name, ProjectMember manager) { +} \ No newline at end of file diff --git a/src/main/java/org/lab/dto/TaskDTO.java b/src/main/java/org/lab/dto/TaskDTO.java new file mode 100644 index 0000000..8cb7037 --- /dev/null +++ b/src/main/java/org/lab/dto/TaskDTO.java @@ -0,0 +1,6 @@ +package org.lab.dto; + +import org.lab.model.MilestoneModel.Milestone; + +public record TaskDTO(Milestone milestone, String name, String description) { +} diff --git a/src/main/java/org/lab/dto/UserDTO.java b/src/main/java/org/lab/dto/UserDTO.java new file mode 100644 index 0000000..a88c563 --- /dev/null +++ b/src/main/java/org/lab/dto/UserDTO.java @@ -0,0 +1,4 @@ +package org.lab.dto; + +public record UserDTO(String email, String name) { +} diff --git a/src/main/java/org/lab/model/BugReport/BugReport.java b/src/main/java/org/lab/model/BugReport/BugReport.java new file mode 100644 index 0000000..0f778f5 --- /dev/null +++ b/src/main/java/org/lab/model/BugReport/BugReport.java @@ -0,0 +1,18 @@ +package org.lab.model.BugReport; + +import org.lab.model.ProjectModel.Project; +import org.lab.model.TicketModel.Ticket; + +public final class BugReport extends Ticket { + private final Project project; + + public BugReport(String id, Project project, String name, String description) { + super(id, name, description, BugReportStatus.OPEN); + + this.project = project; + } + + public Project getProject() { + return project; + } +} diff --git a/src/main/java/org/lab/model/BugReport/BugReportRole.java b/src/main/java/org/lab/model/BugReport/BugReportRole.java new file mode 100644 index 0000000..5dc4232 --- /dev/null +++ b/src/main/java/org/lab/model/BugReport/BugReportRole.java @@ -0,0 +1,7 @@ +package org.lab.model.BugReport; + +public enum BugReportRole { + DEV, + TESTER, + REPORTER, +} diff --git a/src/main/java/org/lab/model/BugReport/BugReportStatus.java b/src/main/java/org/lab/model/BugReport/BugReportStatus.java new file mode 100644 index 0000000..e7372c6 --- /dev/null +++ b/src/main/java/org/lab/model/BugReport/BugReportStatus.java @@ -0,0 +1,8 @@ +package org.lab.model.BugReport; + +public enum BugReportStatus { + OPEN, + FIXED, + TESTED, + CLOSED +} diff --git a/src/main/java/org/lab/model/MilestoneModel/Milestone.java b/src/main/java/org/lab/model/MilestoneModel/Milestone.java new file mode 100644 index 0000000..5f50fee --- /dev/null +++ b/src/main/java/org/lab/model/MilestoneModel/Milestone.java @@ -0,0 +1,79 @@ +package org.lab.model.MilestoneModel; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.lab.model.ProjectModel.Project; +import org.lab.model.TaskModel.Task; + +public class Milestone { + private final String id; + private final Project project; + private String name; + private LocalDate start; + private LocalDate end; + private MilestoneStatus status; + + private final List tasks; + + public Milestone(String id, Project project, String name, LocalDate start, LocalDate end) { + this.id = Objects.requireNonNull(id); + this.project = project; + this.name = Objects.requireNonNull(name); + this.start = Objects.requireNonNull(start); + this.end = Objects.requireNonNull(end); + status = MilestoneStatus.OPEN; + tasks = new ArrayList<>(); + } + + public String getId() { + return id; + } + + public Project getProject() { + return project; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = Objects.requireNonNull(name); + } + + public LocalDate getStart() { + return start; + } + + public void setStart(LocalDate start) { + this.start = Objects.requireNonNull(start); + } + + public LocalDate getEnd() { + return end; + } + + public void setEnd(LocalDate end) { + this.end = Objects.requireNonNull(end); + } + + public MilestoneStatus getStatus() { + return status; + } + + public void setStatus(MilestoneStatus status) { + this.status = Objects.requireNonNull(status); + } + + public void addTask(Task task) { + tasks.add(Objects.requireNonNull(task)); + } + + public List getTasks() { + return Collections.unmodifiableList(tasks); + } +} diff --git a/src/main/java/org/lab/model/MilestoneModel/MilestoneStatus.java b/src/main/java/org/lab/model/MilestoneModel/MilestoneStatus.java new file mode 100644 index 0000000..1e8c46a --- /dev/null +++ b/src/main/java/org/lab/model/MilestoneModel/MilestoneStatus.java @@ -0,0 +1,7 @@ +package org.lab.model.MilestoneModel; + +public enum MilestoneStatus { + OPEN, + ACTIVE, + CLOSED, +} diff --git a/src/main/java/org/lab/model/ProjectModel/Project.java b/src/main/java/org/lab/model/ProjectModel/Project.java new file mode 100644 index 0000000..602d85b --- /dev/null +++ b/src/main/java/org/lab/model/ProjectModel/Project.java @@ -0,0 +1,122 @@ +package org.lab.model.ProjectModel; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.lab.model.BugReport.BugReport; +import org.lab.model.MilestoneModel.Milestone; + +public class Project { + private String id; + private String name; + private ProjectMember lead; + private ProjectMember manager; + private final Set members; + + private Milestone activeMilestone; + private final Set milestones; + + private final Set bugReports; + + public Project(String id, String name, ProjectMember manager) { + this.id = Objects.requireNonNull(id); + this.name = Objects.requireNonNull(name); + this.manager = Objects.requireNonNull(manager); + + members = new HashSet<>(); + members.add(manager); + + milestones = new HashSet<>(); + bugReports = new HashSet<>(); + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = Objects.requireNonNull(name); + } + + public ProjectMember getLead() { + return lead; + } + + public void setLead(ProjectMember lead) { + if (!members.contains(lead)) { + throw new IllegalArgumentException("user must be in project"); + } + + if (lead.role() != ProjectRole.LEAD) { + throw new IllegalArgumentException("project member must be lead"); + } + + this.lead = lead; + } + + public ProjectMember getManager() { + return manager; + } + + public void setManager(ProjectMember manager) { + if (!members.contains(manager)) { + throw new IllegalArgumentException("user must be in project"); + } + + if (manager.role() != ProjectRole.MANAGER) { + throw new IllegalArgumentException("project member must be manager"); + } + + this.manager = Objects.requireNonNull(manager); + } + + public void addMember(ProjectMember member) { + members.add(member); + } + + public Set getMembers() { + return members; + } + + public Milestone getActiveMilestone() { + return activeMilestone; + } + + public void setActiveMilestone(Milestone activeMilestone) { + if (!milestones.contains(activeMilestone)) { + throw new IllegalArgumentException("milestones must be in project"); + } + + this.activeMilestone = activeMilestone; + } + + public void addMilestone(Milestone milestone) { + if (!milestone.getProject().getId().equals(id)) { + String message = new StringBuilder() + .append("cannot add ").append(milestone.getId()).append(" to project ").append(id).append((". ")) + .append(milestone.getId()).append(" already in project ").append(milestone.getProject().getId()) + .toString(); + + throw new IllegalArgumentException(message); + } + + milestones.add(milestone); + } + + public Set getMilestones() { + return milestones; + } + + public void attachBugReport(BugReport report) { + bugReports.add(report); + } + + public Set getBugReports() { + return bugReports; + } +} diff --git a/src/main/java/org/lab/model/ProjectModel/ProjectMember.java b/src/main/java/org/lab/model/ProjectModel/ProjectMember.java new file mode 100644 index 0000000..cb831ba --- /dev/null +++ b/src/main/java/org/lab/model/ProjectModel/ProjectMember.java @@ -0,0 +1,6 @@ +package org.lab.model.ProjectModel; + +import org.lab.model.UserModel.User; + +public record ProjectMember(User user, ProjectRole role) { +} diff --git a/src/main/java/org/lab/model/ProjectModel/ProjectRole.java b/src/main/java/org/lab/model/ProjectModel/ProjectRole.java new file mode 100644 index 0000000..9b7cdc8 --- /dev/null +++ b/src/main/java/org/lab/model/ProjectModel/ProjectRole.java @@ -0,0 +1,8 @@ +package org.lab.model.ProjectModel; + +public enum ProjectRole { + LEAD, + DEV, + TESTER, + MANAGER, +} diff --git a/src/main/java/org/lab/model/TaskModel/Task.java b/src/main/java/org/lab/model/TaskModel/Task.java new file mode 100644 index 0000000..e22e9d5 --- /dev/null +++ b/src/main/java/org/lab/model/TaskModel/Task.java @@ -0,0 +1,18 @@ +package org.lab.model.TaskModel; + +import org.lab.model.MilestoneModel.Milestone; +import org.lab.model.TicketModel.Ticket; + +public final class Task extends Ticket { + private final Milestone milestone; + + public Task(String id, Milestone milestone, String name, String description) { + super(id, name, description, TaskStatus.OPENED); + + this.milestone = milestone; + } + + public Milestone getMilestone() { + return milestone; + } +} diff --git a/src/main/java/org/lab/model/TaskModel/TaskRole.java b/src/main/java/org/lab/model/TaskModel/TaskRole.java new file mode 100644 index 0000000..3b64cf0 --- /dev/null +++ b/src/main/java/org/lab/model/TaskModel/TaskRole.java @@ -0,0 +1,6 @@ +package org.lab.model.TaskModel; + +public enum TaskRole { + DEV, + TESTER, +} diff --git a/src/main/java/org/lab/model/TaskModel/TaskStatus.java b/src/main/java/org/lab/model/TaskModel/TaskStatus.java new file mode 100644 index 0000000..377ebfe --- /dev/null +++ b/src/main/java/org/lab/model/TaskModel/TaskStatus.java @@ -0,0 +1,8 @@ +package org.lab.model.TaskModel; + +public enum TaskStatus { + OPENED, + READY_FOR_WORK, + IN_PROGESS, + CLOSED, +} diff --git a/src/main/java/org/lab/model/TicketModel/Ticket.java b/src/main/java/org/lab/model/TicketModel/Ticket.java new file mode 100644 index 0000000..b8cd69a --- /dev/null +++ b/src/main/java/org/lab/model/TicketModel/Ticket.java @@ -0,0 +1,74 @@ +package org.lab.model.TicketModel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.lab.model.ProjectModel.ProjectMember; + +public abstract class Ticket { + private String id; + private String name; + private String description; + + private final Map> members; + + private ProjectMember assignee; + private TStatus status; + + public Ticket(String id, String name, String description, TStatus initialStatus) { + this.id = Objects.requireNonNull(id); + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + members = new HashMap<>(); + status = Objects.requireNonNull(initialStatus); + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = Objects.requireNonNull(name); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = Objects.requireNonNull(description); + } + + public void addToTicker(TRole role, ProjectMember member) { + members.putIfAbsent(role, new ArrayList<>()); + members.get(role).add(member); + } + + public Map> getTicketMembers() { + return Collections.unmodifiableMap(members); + } + + public ProjectMember getAssignee() { + return assignee; + } + + public void assign(ProjectMember assignee) { + this.assignee = assignee; + } + + public TStatus getStatus() { + return status; + } + + public void setStatus(TStatus status) { + this.status = Objects.requireNonNull(status); + } +} diff --git a/src/main/java/org/lab/model/UserModel/User.java b/src/main/java/org/lab/model/UserModel/User.java new file mode 100644 index 0000000..07d2ddb --- /dev/null +++ b/src/main/java/org/lab/model/UserModel/User.java @@ -0,0 +1,4 @@ +package org.lab.model.UserModel; + +public record User(String id, String email, String name) { +} diff --git a/src/main/java/org/lab/repositories/BugReportRepo/BugReportRepo.java b/src/main/java/org/lab/repositories/BugReportRepo/BugReportRepo.java new file mode 100644 index 0000000..5c23ef6 --- /dev/null +++ b/src/main/java/org/lab/repositories/BugReportRepo/BugReportRepo.java @@ -0,0 +1,16 @@ +package org.lab.repositories.BugReportRepo; + +import java.util.List; +import java.util.Optional; + +import org.lab.dto.BugReportDTO; +import org.lab.model.BugReport.BugReport; +import org.lab.model.UserModel.User; + +public interface BugReportRepo { + BugReport save(BugReportDTO bugReportDTO); + + List getUserBugReports(User user); + + Optional getBugReportById(String id); +} \ No newline at end of file diff --git a/src/main/java/org/lab/repositories/BugReportRepo/InMemoryBugReportRepo.java b/src/main/java/org/lab/repositories/BugReportRepo/InMemoryBugReportRepo.java new file mode 100644 index 0000000..03a963e --- /dev/null +++ b/src/main/java/org/lab/repositories/BugReportRepo/InMemoryBugReportRepo.java @@ -0,0 +1,37 @@ +package org.lab.repositories.BugReportRepo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.lab.dto.BugReportDTO; +import org.lab.model.BugReport.BugReport; +import org.lab.model.UserModel.User; + +public class InMemoryBugReportRepo implements BugReportRepo { + private final Map store = new HashMap<>(); + + @Override + public BugReport save(BugReportDTO bugReportDTO) { + var uuid = UUID.randomUUID(); + var bugReport = new BugReport(uuid.toString(), bugReportDTO.project(), bugReportDTO.name(), + bugReportDTO.description()); + store.put(uuid, bugReport); + return bugReport; + } + + @Override + public List getUserBugReports(User user) { + return store.values().stream().filter((bugReport) -> bugReport.getAssignee().equals(user)).toList(); + } + + @Override + public Optional getBugReportById(String id) { + var uuid = UUID.fromString(id); + var bugReport = Optional.ofNullable(store.get(uuid)); + return bugReport; + } + +} diff --git a/src/main/java/org/lab/repositories/MilestoneRepo/InMemoryMilestoneRepo.java b/src/main/java/org/lab/repositories/MilestoneRepo/InMemoryMilestoneRepo.java new file mode 100644 index 0000000..8877e11 --- /dev/null +++ b/src/main/java/org/lab/repositories/MilestoneRepo/InMemoryMilestoneRepo.java @@ -0,0 +1,30 @@ +package org.lab.repositories.MilestoneRepo; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.lab.dto.MilestoneDTO; +import org.lab.model.MilestoneModel.Milestone; + +public class InMemoryMilestoneRepo implements MilestoneRepo { + private final Map store = new HashMap<>(); + + @Override + public Milestone save(MilestoneDTO milestoneDTO) { + var uuid = UUID.randomUUID(); + var milestone = new Milestone(uuid.toString(), milestoneDTO.project(), milestoneDTO.name(), + milestoneDTO.start(), milestoneDTO.end()); + store.put(uuid, milestone); + return milestone; + } + + @Override + public Optional getMilestoneById(String id) { + var uuid = UUID.fromString(id); + var milestone = Optional.ofNullable(store.get(uuid)); + return milestone; + } + +} diff --git a/src/main/java/org/lab/repositories/MilestoneRepo/MilestoneRepo.java b/src/main/java/org/lab/repositories/MilestoneRepo/MilestoneRepo.java new file mode 100644 index 0000000..b46c9f2 --- /dev/null +++ b/src/main/java/org/lab/repositories/MilestoneRepo/MilestoneRepo.java @@ -0,0 +1,12 @@ +package org.lab.repositories.MilestoneRepo; + +import java.util.Optional; + +import org.lab.dto.MilestoneDTO; +import org.lab.model.MilestoneModel.Milestone; + +public interface MilestoneRepo { + Milestone save(MilestoneDTO milestoneDTO); + + Optional getMilestoneById(String id); +} diff --git a/src/main/java/org/lab/repositories/ProjectRepo/InMemoryProjectRepo.java b/src/main/java/org/lab/repositories/ProjectRepo/InMemoryProjectRepo.java new file mode 100644 index 0000000..66fb31f --- /dev/null +++ b/src/main/java/org/lab/repositories/ProjectRepo/InMemoryProjectRepo.java @@ -0,0 +1,39 @@ +package org.lab.repositories.ProjectRepo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.lab.dto.ProjectDTO; +import org.lab.model.ProjectModel.Project; +import org.lab.model.UserModel.User; + +public class InMemoryProjectRepo implements ProjectRepo { + private final Map store = new HashMap<>(); + + @Override + public Project save(ProjectDTO projectDTO) { + var uuid = UUID.randomUUID(); + var project = new Project(uuid.toString(), projectDTO.name(), projectDTO.manager()); + store.put(uuid, project); + return project; + } + + @Override + public Optional getProjectById(String id) { + var uuid = UUID.fromString(id); + var project = Optional.ofNullable(store.get(uuid)); + return project; + } + + @Override + public List getUserProjects(User user) { + return store.values().stream() + .filter((project) -> project.getMembers().stream() + .anyMatch((projectMember) -> projectMember.user().equals(user))) + .toList(); + } + +} diff --git a/src/main/java/org/lab/repositories/ProjectRepo/ProjectRepo.java b/src/main/java/org/lab/repositories/ProjectRepo/ProjectRepo.java new file mode 100644 index 0000000..7f76a8e --- /dev/null +++ b/src/main/java/org/lab/repositories/ProjectRepo/ProjectRepo.java @@ -0,0 +1,16 @@ +package org.lab.repositories.ProjectRepo; + +import java.util.List; +import java.util.Optional; + +import org.lab.dto.ProjectDTO; +import org.lab.model.ProjectModel.Project; +import org.lab.model.UserModel.User; + +public interface ProjectRepo { + Project save(ProjectDTO projectDTO); + + Optional getProjectById(String id); + + List getUserProjects(User user); +} diff --git a/src/main/java/org/lab/repositories/TaskRepo/InMemoryTaskRepo.java b/src/main/java/org/lab/repositories/TaskRepo/InMemoryTaskRepo.java new file mode 100644 index 0000000..7aac97c --- /dev/null +++ b/src/main/java/org/lab/repositories/TaskRepo/InMemoryTaskRepo.java @@ -0,0 +1,35 @@ +package org.lab.repositories.TaskRepo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.lab.dto.TaskDTO; +import org.lab.model.TaskModel.Task; +import org.lab.model.UserModel.User; + +public class InMemoryTaskRepo implements TaskRepo { + private final Map store = new HashMap<>(); + + @Override + public Task save(TaskDTO taskDTO) { + var uuid = UUID.randomUUID(); + var task = new Task(uuid.toString(), taskDTO.milestone(), taskDTO.name(), taskDTO.description()); + store.put(uuid, task); + return task; + } + + @Override + public List getUserTasks(User user) { + return store.values().stream().filter((task) -> task.getAssignee().equals(user)).toList(); + } + + @Override + public Optional getTaskById(String id) { + var uuid = UUID.fromString(id); + var task = Optional.ofNullable(store.get(uuid)); + return task; + } +} diff --git a/src/main/java/org/lab/repositories/TaskRepo/TaskRepo.java b/src/main/java/org/lab/repositories/TaskRepo/TaskRepo.java new file mode 100644 index 0000000..dad8a2f --- /dev/null +++ b/src/main/java/org/lab/repositories/TaskRepo/TaskRepo.java @@ -0,0 +1,16 @@ +package org.lab.repositories.TaskRepo; + +import java.util.List; +import java.util.Optional; + +import org.lab.dto.TaskDTO; +import org.lab.model.TaskModel.Task; +import org.lab.model.UserModel.User; + +public interface TaskRepo { + Task save(TaskDTO taskDTO); + + List getUserTasks(User user); + + Optional getTaskById(String id); +} diff --git a/src/main/java/org/lab/repositories/UserRepo/InMemoryUserRepo.java b/src/main/java/org/lab/repositories/UserRepo/InMemoryUserRepo.java new file mode 100644 index 0000000..ee359cb --- /dev/null +++ b/src/main/java/org/lab/repositories/UserRepo/InMemoryUserRepo.java @@ -0,0 +1,29 @@ +package org.lab.repositories.UserRepo; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.lab.dto.UserDTO; +import org.lab.model.UserModel.User; + +public class InMemoryUserRepo implements UserRepo { + private final Map store = new HashMap<>(); + + @Override + public User save(UserDTO userDTO) { + var uuid = UUID.randomUUID(); + var user = new User(uuid.toString(), userDTO.email(), userDTO.name()); + store.put(uuid, user); + return user; + } + + @Override + public Optional getUserById(String id) { + var uuid = UUID.fromString(id); + var user = Optional.ofNullable(store.get(uuid)); + return user; + } + +} diff --git a/src/main/java/org/lab/repositories/UserRepo/UserRepo.java b/src/main/java/org/lab/repositories/UserRepo/UserRepo.java new file mode 100644 index 0000000..ad9042d --- /dev/null +++ b/src/main/java/org/lab/repositories/UserRepo/UserRepo.java @@ -0,0 +1,12 @@ +package org.lab.repositories.UserRepo; + +import java.util.Optional; + +import org.lab.dto.UserDTO; +import org.lab.model.UserModel.User; + +public interface UserRepo { + User save(UserDTO userDTO); + + Optional getUserById(String id); +} diff --git a/src/main/java/org/lab/services/AccessControl.java b/src/main/java/org/lab/services/AccessControl.java new file mode 100644 index 0000000..7b40d8e --- /dev/null +++ b/src/main/java/org/lab/services/AccessControl.java @@ -0,0 +1,213 @@ +package org.lab.services; + +import org.lab.model.BugReport.BugReport; +import org.lab.model.MilestoneModel.Milestone; +import org.lab.model.ProjectModel.Project; +import org.lab.model.ProjectModel.ProjectRole; +import org.lab.model.TaskModel.Task; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.MilestoneRepo.MilestoneRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class AccessControl { + private final UserRepo userRepo; + private final ProjectRepo projectRepo; + private final MilestoneRepo milestoneRepo; + private final TaskRepo taskRepo; + private final BugReportRepo bugReportRepo; + + public AccessControl(UserRepo userRepo, ProjectRepo projectRepo, MilestoneRepo milestoneRepo, TaskRepo taskRepo, + BugReportRepo bugReportRepo) { + this.userRepo = userRepo; + this.projectRepo = projectRepo; + this.milestoneRepo = milestoneRepo; + this.taskRepo = taskRepo; + this.bugReportRepo = bugReportRepo; + } + + public Project getProjectForAddBugReportIfHasAccess(String userId, String projectId) { + var tester = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var projectToReport = projectRepo.getProjectById(projectId) + .map((project) -> project) + .orElseThrow(() -> new IllegalArgumentException("no such project " + projectId)); + + var testerProjectMember = projectToReport.getMembers().stream() + .filter((projectMember) -> projectMember.user().equals(tester)).findFirst() + .map((projectMember) -> projectMember) + .orElseThrow( + () -> new IllegalArgumentException( + "no such user " + tester + " in project " + + projectToReport.getId())); + + if (testerProjectMember.role() != ProjectRole.TESTER && testerProjectMember.role() != ProjectRole.DEV) { + throw new IllegalArgumentException( + "user " + userId + " is not tester or dev of project " + projectId); + } + + return projectToReport; + } + + public Project getProjectForUpdateLeadIfHasAccess(String userId, String projectId) { + var manager = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var managerProject = projectRepo.getProjectById(projectId) + .map((project) -> project) + .orElseThrow(() -> new IllegalArgumentException("no such project " + projectId)); + + if (!managerProject.getManager().user().equals(manager)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager of project " + projectId); + } + + return managerProject; + } + + public Project getProjectForAddMemberIfHasAccess(String userId, String projectId) { + var manager = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var managerProject = projectRepo.getProjectById(projectId) + .map((project) -> project) + .orElseThrow(() -> new IllegalArgumentException("no such project " + projectId)); + + if (!managerProject.getManager().user().equals(manager)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager of project " + projectId); + } + + return managerProject; + } + + public Project getProjectForCreateMilestoneIfHasAccess(String userId, String projectId) { + var manager = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var managerProject = projectRepo.getProjectById(projectId) + .map((project) -> project) + .orElseThrow(() -> new IllegalArgumentException("no such project " + projectId)); + + if (!managerProject.getManager().user().equals(manager)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager of project " + projectId); + } + + return managerProject; + } + + public Milestone getMilestoneIfHasAccess(String userId, String milestoneId) { + var manager = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var projectMilestone = milestoneRepo.getMilestoneById(milestoneId) + .map((milestone) -> milestone) + .orElseThrow(() -> new IllegalArgumentException("no such milestone " + milestoneId)); + + var project = projectMilestone.getProject(); + + if (!project.getManager().user().equals(manager)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager of project " + project.getId()); + } + + return projectMilestone; + } + + public Task getTaskForMembersUpdateIfHasAccess(String userId, String taskId) { + var updater = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var task = taskRepo.getTaskById(taskId).map((t) -> t) + .orElseThrow(() -> new IllegalArgumentException("no such task " + taskId)); + + var project = task.getMilestone().getProject(); + + if (!project.getManager().user().equals(updater) && !project.getLead().user().equals(updater)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager or lead of project " + project.getId()); + } + + return task; + } + + public BugReport getBugReportForMembersUpdateIfHasAccess(String userId, String bugReportId) { + var updater = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var bugReport = bugReportRepo.getBugReportById(bugReportId).map((r) -> r) + .orElseThrow(() -> new IllegalArgumentException("no such task " + bugReportId)); + + var project = bugReport.getProject(); + + if (!project.getManager().user().equals(updater) && !project.getLead().user().equals(updater)) { + throw new IllegalArgumentException( + "user " + userId + " is not manager or lead of project " + project.getId()); + } + + return bugReport; + } + + public Task getTaskForStatusUpdateIfHasAccess(String userId, String taskId) { + var updater = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var task = taskRepo.getTaskById(taskId).map((t) -> t) + .orElseThrow(() -> new IllegalArgumentException("no such task " + taskId)); + + var project = task.getMilestone().getProject(); + + var updaterProjectMember = project.getMembers().stream() + .filter((projectMember) -> projectMember.user().equals(updater)).findFirst() + .map((projectMember) -> projectMember) + .orElseThrow( + () -> new IllegalArgumentException( + "no such user " + updater + " in project " + + project.getId())); + + if (updaterProjectMember.role() != ProjectRole.DEV && updaterProjectMember.role() != ProjectRole.LEAD) { + throw new IllegalArgumentException( + "user " + userId + " is not dev of project " + project.getId()); + } + + return task; + } + + public BugReport getBugReportForStatusUpdateIfHasAccess(String userId, String bugReportId) { + var updater = userRepo.getUserById(userId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + + var bugReport = bugReportRepo.getBugReportById(bugReportId).map((t) -> t) + .orElseThrow(() -> new IllegalArgumentException("no such bug report " + bugReportId)); + + var project = bugReport.getProject(); + + var updaterProjectMember = project.getMembers().stream() + .filter((projectMember) -> projectMember.user().equals(updater)).findFirst() + .map((projectMember) -> projectMember) + .orElseThrow( + () -> new IllegalArgumentException( + "no such user " + updater + " in project " + + project.getId())); + + if (updaterProjectMember.role() != ProjectRole.DEV + && updaterProjectMember.role() != ProjectRole.TESTER) { + throw new IllegalArgumentException( + "user " + userId + " is not dev or tester of project " + project.getId()); + } + + return bugReport; + } +} diff --git a/src/main/java/org/lab/services/BugReportService.java b/src/main/java/org/lab/services/BugReportService.java new file mode 100644 index 0000000..33a4d4e --- /dev/null +++ b/src/main/java/org/lab/services/BugReportService.java @@ -0,0 +1,62 @@ +package org.lab.services; + +import org.lab.dto.BugReportDTO; +import org.lab.model.BugReport.BugReportRole; +import org.lab.model.BugReport.BugReportStatus; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.MilestoneRepo.MilestoneRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class BugReportService { + private final UserRepo userRepo; + private final BugReportRepo bugReportRepo; + + private final AccessControl accessControl; + + public BugReportService(UserRepo userRepo, ProjectRepo projectRepo, MilestoneRepo milestoneRepo, + TaskRepo taskRepo, BugReportRepo bugReportRepo) { + this.userRepo = userRepo; + this.bugReportRepo = bugReportRepo; + + this.accessControl = new AccessControl(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + } + + public String createBugReport(String creatorId, String projectId, String name, String description) { + var project = accessControl.getProjectForAddBugReportIfHasAccess(creatorId, projectId); + var bugReportDTO = new BugReportDTO(project, name, description); + var bugReport = bugReportRepo.save(bugReportDTO); + project.attachBugReport(bugReport); + return bugReport.getId(); + } + + public void addMemberToBugReport(String updaterId, String taskId, String memberId, BugReportRole role) { + var testerUser = userRepo.getUserById(memberId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + memberId)); + + var bugReport = accessControl.getBugReportForMembersUpdateIfHasAccess(updaterId, taskId); + var project = bugReport.getProject(); + + var projectMember = project.getMembers().stream() + .filter((p) -> p.user().equals(testerUser)).findFirst() + .map((p) -> p) + .orElseThrow( + () -> new IllegalArgumentException( + "no such user " + memberId + " in project " + project.getId())); + + bugReport.addToTicker(role, projectMember); + } + + public BugReportStatus getBugReportStatus(String bugReportId) { + return bugReportRepo.getBugReportById(bugReportId) + .map((report) -> report.getStatus()) + .orElseThrow(() -> new IllegalArgumentException("no such bug report " + bugReportId)); + } + + public void updateBugReportStatus(String updaterId, String taskId, BugReportStatus status) { + var bugReport = accessControl.getBugReportForStatusUpdateIfHasAccess(updaterId, taskId); + bugReport.setStatus(status); + } +} diff --git a/src/main/java/org/lab/services/MilestoneService.java b/src/main/java/org/lab/services/MilestoneService.java new file mode 100644 index 0000000..869fde6 --- /dev/null +++ b/src/main/java/org/lab/services/MilestoneService.java @@ -0,0 +1,37 @@ +package org.lab.services; + +import java.time.LocalDate; + +import org.lab.dto.MilestoneDTO; +import org.lab.model.MilestoneModel.MilestoneStatus; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.MilestoneRepo.MilestoneRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class MilestoneService { + private final MilestoneRepo milestoneRepo; + + private final AccessControl accessControl; + + public MilestoneService(UserRepo userRepo, ProjectRepo projectRepo, MilestoneRepo milestoneRepo, + TaskRepo taskRepo, BugReportRepo bugReportRepo) { + this.milestoneRepo = milestoneRepo; + + this.accessControl = new AccessControl(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + } + + public String createMilestone(String managerId, String projectId, String name, LocalDate start, LocalDate end) { + var project = accessControl.getProjectForCreateMilestoneIfHasAccess(managerId, projectId); + var milestoneDTO = new MilestoneDTO(project, name, start, end); + var milestone = milestoneRepo.save(milestoneDTO); + project.addMilestone(milestone); + return milestone.getId(); + } + + public void updateMilestoneStatus(String managerId, String milestoneId, MilestoneStatus status) { + var milestone = accessControl.getMilestoneIfHasAccess(managerId, milestoneId); + milestone.setStatus(status); + } +} diff --git a/src/main/java/org/lab/services/ProjectService.java b/src/main/java/org/lab/services/ProjectService.java new file mode 100644 index 0000000..cacdafd --- /dev/null +++ b/src/main/java/org/lab/services/ProjectService.java @@ -0,0 +1,56 @@ +package org.lab.services; + +import org.lab.dto.ProjectDTO; +import org.lab.model.ProjectModel.ProjectMember; +import org.lab.model.ProjectModel.ProjectRole; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.MilestoneRepo.MilestoneRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class ProjectService { + private final UserRepo userRepo; + private final ProjectRepo projectRepo; + + private final AccessControl accessControl; + + public ProjectService(UserRepo userRepo, ProjectRepo projectRepo, MilestoneRepo milestoneRepo, TaskRepo taskRepo, + BugReportRepo bugReportRepo) { + this.userRepo = userRepo; + this.projectRepo = projectRepo; + + this.accessControl = new AccessControl(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + } + + public String createProject(String name, String managerUserId) { + return userRepo.getUserById(managerUserId) + .map((user) -> { + var projectManager = new ProjectMember(user, ProjectRole.MANAGER); + var projectDTO = new ProjectDTO(name, projectManager); + return projectRepo.save(projectDTO).getId(); + }) + .orElseThrow(() -> new IllegalArgumentException("no such user " + managerUserId)); + } + + public void updateLead(String managerId, String projectId, String leadId) { + var project = accessControl.getProjectForUpdateLeadIfHasAccess(managerId, projectId); + var leadUser = userRepo.getUserById(leadId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + leadId)); + + var projectLead = new ProjectMember(leadUser, ProjectRole.LEAD); + project.addMember(projectLead); + project.setLead(projectLead); + } + + public void addMember(String managerId, String projectId, String memberId, ProjectRole role) { + var project = accessControl.getProjectForAddMemberIfHasAccess(managerId, projectId); + var memberUser = userRepo.getUserById(memberId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + memberId)); + + var projectLead = new ProjectMember(memberUser, role); + project.addMember(projectLead); + } +} diff --git a/src/main/java/org/lab/services/TaskService.java b/src/main/java/org/lab/services/TaskService.java new file mode 100644 index 0000000..9465145 --- /dev/null +++ b/src/main/java/org/lab/services/TaskService.java @@ -0,0 +1,62 @@ +package org.lab.services; + +import org.lab.dto.TaskDTO; +import org.lab.model.TaskModel.TaskRole; +import org.lab.model.TaskModel.TaskStatus; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.MilestoneRepo.MilestoneRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class TaskService { + private final UserRepo userRepo; + private final TaskRepo taskRepo; + + private final AccessControl accessControl; + + public TaskService(UserRepo userRepo, ProjectRepo projectRepo, MilestoneRepo milestoneRepo, + TaskRepo taskRepo, BugReportRepo bugReportRepo) { + this.userRepo = userRepo; + this.taskRepo = taskRepo; + + this.accessControl = new AccessControl(userRepo, projectRepo, milestoneRepo, taskRepo, bugReportRepo); + } + + public String createTask(String creatorId, String milestoneId, String name, String description) { + var milestone = accessControl.getMilestoneIfHasAccess(creatorId, milestoneId); + var taskDTO = new TaskDTO(milestone, name, description); + var task = taskRepo.save(taskDTO); + milestone.addTask(task); + return task.getId(); + } + + public void addMemberToTask(String updaterId, String taskId, String memberId, TaskRole role) { + var devUser = userRepo.getUserById(memberId) + .map((user) -> user) + .orElseThrow(() -> new IllegalArgumentException("no such user " + memberId)); + + var task = accessControl.getTaskForMembersUpdateIfHasAccess(updaterId, taskId); + var project = task.getMilestone().getProject(); + + var projectMember = project.getMembers().stream() + .filter((p) -> p.user().equals(devUser)).findFirst() + .map((p) -> p) + .orElseThrow( + () -> new IllegalArgumentException( + "no such user " + memberId + " in project " + project.getId())); + + task.addToTicker(role, projectMember); + } + + public TaskStatus getTaskStatus(String taskId) { + return taskRepo.getTaskById(taskId) + .map((task) -> task.getStatus()) + .orElseThrow(() -> new IllegalArgumentException("no such task " + taskId)); + } + + public void updateTaskStatus(String updaterId, String taskId, TaskStatus status) { + var task = accessControl.getTaskForStatusUpdateIfHasAccess(updaterId, taskId); + task.setStatus(status); + } +} diff --git a/src/main/java/org/lab/services/UserService.java b/src/main/java/org/lab/services/UserService.java new file mode 100644 index 0000000..80ad580 --- /dev/null +++ b/src/main/java/org/lab/services/UserService.java @@ -0,0 +1,57 @@ +package org.lab.services; + +import java.util.List; + +import org.lab.dto.UserDTO; +import org.lab.model.BugReport.BugReport; +import org.lab.model.ProjectModel.Project; +import org.lab.model.TaskModel.Task; +import org.lab.repositories.BugReportRepo.BugReportRepo; +import org.lab.repositories.ProjectRepo.ProjectRepo; +import org.lab.repositories.TaskRepo.TaskRepo; +import org.lab.repositories.UserRepo.UserRepo; + +public class UserService { + private final UserRepo userRepo; + private final ProjectRepo projectRepo; + private final TaskRepo ticketRepo; + private final BugReportRepo bugReportRepo; + + public UserService(UserRepo userRepo, ProjectRepo projectRepo, TaskRepo taskRepo, BugReportRepo bugReportRepo) { + this.userRepo = userRepo; + this.projectRepo = projectRepo; + this.ticketRepo = taskRepo; + this.bugReportRepo = bugReportRepo; + } + + public String registerUser(String name, String email) { + if (name == "") { + throw new IllegalArgumentException("user name cannot be empty"); + } + + if (email == "") { + throw new IllegalArgumentException("user email cannot be empty"); + } + + var userDTO = new UserDTO(email, name); + return userRepo.save(userDTO).id(); + } + + public List getUserProjects(String userId) { + return userRepo.getUserById(userId) + .map((user) -> projectRepo.getUserProjects(user)) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + } + + List getUserTasks(String userId) { + return userRepo.getUserById(userId) + .map((user) -> ticketRepo.getUserTasks(user)) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + } + + List getUserBugReports(String userId) { + return userRepo.getUserById(userId) + .map((user) -> bugReportRepo.getUserBugReports(user)) + .orElseThrow(() -> new IllegalArgumentException("no such user " + userId)); + } +}