Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/copilot.data.migration.agent.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/copilot.data.migration.ask.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/copilot.data.migration.ask2agent.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/copilot.data.migration.edit.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

# Цели и задачи л/р:
Expand Down
21 changes: 21 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
plugins {
id("java")
id("application")
}

group = "org.lab"
version = "1.0-SNAPSHOT"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(26)
}
}

repositories {
mavenCentral()
}
Expand All @@ -15,6 +22,20 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("--enable-preview")
options.release = 26
}

tasks.test {
useJUnitPlatform()
jvmArgs("--enable-preview")
}

application {
mainClass = "org.lab.Main"
}

tasks.withType<JavaExec>().configureEach {
jvmArgs("--enable-preview")
}
Empty file modified gradlew
100644 → 100755
Empty file.
13 changes: 13 additions & 0 deletions src/main/java/org/lab/IO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.lab;

public final class IO {
private IO() {}

public static void println(Object o) {
System.out.println(o);
}
}




116 changes: 114 additions & 2 deletions src/main/java/org/lab/Main.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,116 @@
void main() {
IO.println("Hello and welcome!");
package org.lab;

import org.lab.domain.CommandResult;
import org.lab.domain.DomainException;
import org.lab.domain.Ticket;
import org.lab.repo.BugRepo;
import org.lab.repo.MilestoneRepo;
import org.lab.repo.ProjectRepo;
import org.lab.repo.TicketRepo;
import org.lab.repo.UserRepo;
import org.lab.service.AuthService;
import org.lab.service.BugService;
import org.lab.service.Context;
import org.lab.service.DashboardService;
import org.lab.service.MilestoneService;
import org.lab.service.ProjectService;
import org.lab.service.TicketService;

import java.time.LocalDate;
import java.util.stream.Gatherers;

public class Main {
public static void main(String[] args) {
var userRepo = new UserRepo();
var projectRepo = new ProjectRepo();
var milestoneRepo = new MilestoneRepo();
var ticketRepo = new TicketRepo();
var bugRepo = new BugRepo();


var authService = new AuthService(userRepo);
var projectService = new ProjectService(projectRepo);
var milestoneService = new MilestoneService(projectRepo, milestoneRepo, ticketRepo);
var ticketService = new TicketService(projectRepo, ticketRepo, milestoneService);
var bugService = new BugService(projectRepo, bugRepo);
var dashboardService = new DashboardService(projectService, ticketService, bugService);


var manager = authService.register("manager");
var teamlead = authService.register("teamlead");
var dev = authService.register("dev");
var tester = authService.register("tester");


var project = projectService.createProject(manager.id(), "Modern Java Project");
var assignLead = projectService.assignTeamLead(manager.id(), project.id(), teamlead.id());


// Pattern matching for switch
var assignLeadMsg = switch (assignLead) {
case CommandResult.Ok(var updatedProject) -> "teamlead=" + updatedProject.teamLeadId();
case CommandResult.Error(var message) -> "error=" + message;
};
IO.println(assignLeadMsg);
// IO

projectService.addDeveloper(manager.id(), project.id(), dev.id());
projectService.addTester(manager.id(), project.id(), tester.id());

var milestone = milestoneService.createMilestone(
manager.id(),
project.id(),
"M1",
LocalDate.now(),
LocalDate.now().plusDays(14)
);
milestoneService.activateMilestone(manager.id(), milestone.id());

var ticket = ticketService.createTicket(teamlead.id(), project.id(), milestone.id(), "Implement ticket flow");
ticketService.assignDeveloper(teamlead.id(), ticket.id(), dev.id());
ticketService.startWork(dev.id(), ticket.id());
ticketService.complete(dev.id(), ticket.id());

var bug = bugService.createBug(tester.id(), project.id(), "NullPointerException in report");
bugService.assignFixer(teamlead.id(), bug.id(), dev.id());
bugService.markFixed(dev.id(), bug.id());
bugService.markTested(tester.id(), bug.id());
bugService.close(manager.id(), bug.id());

try {
// Scoped Values
ScopedValue.where(Context.CURRENT_USER_ID, dev.id()).run(() -> {
var overview = dashboardService.overviewStructured(Context.CURRENT_USER_ID.get());


// Stream Gatherers
var windows = overview.assignedTickets().stream()
.map(Ticket::title)
.gather(Gatherers.windowFixed(2))
.toList();

// Pattern Matching for instanceof
if (windows instanceof java.util.List<?> list) {
var report = """
user=%s
projects=%d
assignedTickets=%d
bugsToFix=%d
ticketTitleWindows=%s
""".formatted(
dev.name(),
overview.projects().size(),
overview.assignedTickets().size(),
overview.bugsToFix().size(),
list
);
// Text Blocks
IO.println(report);
}
});
} catch (DomainException e) {
IO.println("Domain error: " + e.getMessage());
}
}
}

18 changes: 18 additions & 0 deletions src/main/java/org/lab/domain/BugReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.lab.domain;

import java.util.UUID;

public record BugReport(
UUID id,
UUID projectId,
String title,
BugStatus status,
UUID reporterId,
UUID fixerId
) {
// Records
}




12 changes: 12 additions & 0 deletions src/main/java/org/lab/domain/BugStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.lab.domain;

public enum BugStatus {
NEW,
FIXED,
TESTED,
CLOSED
}




16 changes: 16 additions & 0 deletions src/main/java/org/lab/domain/CommandResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.lab.domain;

public sealed interface CommandResult<T> permits CommandResult.Ok, CommandResult.Error {
// Sealed Classes

record Ok<T>(T value) implements CommandResult<T> {
// Records
}

record Error<T>(String message) implements CommandResult<T> {
// Records
}
}



15 changes: 15 additions & 0 deletions src/main/java/org/lab/domain/DomainException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.lab.domain;

public final class DomainException extends RuntimeException {
public DomainException(String message) {
// Flexible Constructor Bodies
if (message == null || message.isBlank()) {
throw new IllegalArgumentException("message is blank");
}
super(message);
}
}




21 changes: 21 additions & 0 deletions src/main/java/org/lab/domain/Milestone.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.lab.domain;

import java.time.LocalDate;
import java.util.List;
import java.util.UUID;

public record Milestone(
UUID id,
UUID projectId,
String name,
LocalDate startDate,
LocalDate endDate,
MilestoneStatus status,
List<UUID> ticketIds
) {
// Records
}




11 changes: 11 additions & 0 deletions src/main/java/org/lab/domain/MilestoneStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.lab.domain;

public enum MilestoneStatus {
OPEN,
ACTIVE,
CLOSED
}




34 changes: 34 additions & 0 deletions src/main/java/org/lab/domain/Project.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.lab.domain;

import java.util.Set;
import java.util.UUID;

public record Project(
ProjectId id,
String name,
UUID managerId,
UUID teamLeadId,
Set<UUID> developerIds,
Set<UUID> testerIds,
UUID activeMilestoneId
) {
// Records

public ProjectRole roleFor(UUID userId) {
// Pattern Matching for switch
return switch (userId) {
case UUID id when id.equals(managerId) -> ProjectRole.MANAGER;
case UUID id when teamLeadId != null && id.equals(teamLeadId) -> ProjectRole.TEAMLEAD;
case UUID id when developerIds.contains(id) -> ProjectRole.DEVELOPER;
case UUID id when testerIds.contains(id) -> ProjectRole.TESTER;
default -> null;
};
}

public boolean isParticipant(UUID userId) {
return roleFor(userId) != null;
}
}



Loading