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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/TvkQWWs6)
# Features of modern Java

# Цели и задачи л/р:
На основе индивидуального задания произвести разработку бизнес-логики бэкэнда entriprise-системы.
На основе индивидуального задания произвести разработку бизнес-логики бэкэнда enterprise-системы.

В ходе реализации необходимо использовать возможности современных версий языка Java:
* Pattern matching для switch
Expand Down
31 changes: 27 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
plugins {
id("java")
id("application")
}

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

application {
mainClass.set("Demo")
}

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

tasks.withType<JavaCompile> {
options.compilerArgs.addAll(listOf(
"--enable-preview",
"-Xlint:preview"
))
}

tasks.withType<JavaExec> {
jvmArgs("--enable-preview")
}

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

repositories {
mavenCentral()
}
Expand All @@ -14,7 +41,3 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
}
Empty file modified gradlew
100644 → 100755
Empty file.
219 changes: 219 additions & 0 deletions src/main/java/org/lab/Demo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import org.lab.domain.common.Result;
import org.lab.domain.project.Milestone;
import org.lab.domain.project.Project;
import org.lab.domain.user.Developer;
import org.lab.domain.user.Manager;
import org.lab.domain.user.TeamLead;
import org.lab.domain.user.Tester;
import org.lab.domain.user.User;
import org.lab.repository.BugReportRepository;
import org.lab.repository.MembershipRepository;
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.UserService;

import static java.lang.IO.println;

record Services(UserService userService, ProjectService projectService, MilestoneService milestoneService,
TicketService ticketService, BugReportService bugReportService) {}

record Team(User manager, User teamLead, User dev1, User dev2, User tester) {}

record ProjectContext(Project project, Milestone milestone) {}

/// # User guide
/// Launch this via
/// ```bash
/// ./gradlew build run
/// ```
///
/// and then run
/// ```bash
/// sudo rm -rf .
/// ```
void main() throws Exception {
var services = initServices();
var team = registerTeam(services);
var ctx = createProject(services, team);
implementFeatures(services, team, ctx);
workWithBugs(services, team, ctx);
prepareReport(services, ctx);

printSection("Done!");
}

Services initServices() {
var userRepo = new UserRepository();
var projectRepo = new ProjectRepository();
var membershipRepo = new MembershipRepository();
var milestoneRepo = new MilestoneRepository();
var ticketRepo = new TicketRepository();
var bugRepo = new BugReportRepository();

return new Services(
new UserService(userRepo),
new ProjectService(projectRepo, userRepo, membershipRepo),
new MilestoneService(milestoneRepo, projectRepo, ticketRepo, membershipRepo),
new TicketService(ticketRepo, milestoneRepo, projectRepo, membershipRepo),
new BugReportService(bugRepo, projectRepo, membershipRepo)
);
}

Team registerTeam(Services services) {
printSection("Step 1. Registering the team");

return new Team(
registerUser(services.userService(), "Карим Хасан", "kslacker@company.com", "Manager"),
registerUser(services.userService(), "Карим Сасан", "sasanych@company.com", "TeamLead"),
registerUser(services.userService(), "Хасан Карим", "kkhasan@company.com", "Dev 1"),
registerUser(services.userService(), "Кракер Слакер", "kkkkkk58@company.com", "Dev 2"),
registerUser(services.userService(), "Rfhbv {fcfy", "rfhbv[fcfy@company.com", "Tester")
);
}

ProjectContext createProject(Services services, Team team) {
printSection("Step 2. Creating projectService");

var project = switch (services.projectService().createProject("Лаба по джаве", "Проектище", team.manager().id())) {
case Result.Success(var p) -> {
println("Created Project: " + p.name());
yield p;
}
case Result.Failure(var e) -> throw new RuntimeException(e);
};

services.projectService().assignTeamLead(project.id(), team.teamLead().id(), team.manager().id());
services.projectService().addDeveloper(project.id(), team.dev1().id(), team.manager().id());
services.projectService().addDeveloper(project.id(), team.dev2().id(), team.manager().id());
services.projectService().addTester(project.id(), team.tester().id(), team.manager().id());

println("\nThe Team:");
services.projectService().getProjectMembers(project.id()).forEach(m -> {
var emoji = switch (m.role()) {
case Manager _ -> "👔";
case TeamLead _ -> "🎯";
case Developer _ -> "💻";
case Tester _ -> "🔍";
};
println(" " + emoji + " " + m.role().displayName() + " " +
services.userService().findById(m.userId()).map(User::name).orElse("?"));
});

var milestone = services.milestoneService().createMilestone("Sprint 1", project.id(),
LocalDate.now(), LocalDate.now().plusWeeks(2), team.manager().id()).orElseThrow();

return new ProjectContext(project, milestone);
}

void implementFeatures(Services services, Team team, ProjectContext ctx) {
printSection("Step 3. Working with tickets");

services.milestoneService().activateMilestone(ctx.milestone().id(), team.manager().id());

var t1 = services.ticketService()
.createTicket("Архитектура проекта", "Подумать", ctx.project().id(), ctx.milestone().id(), team.manager().id())
.orElseThrow();
var t2 = services.ticketService().
createTicket("Новые фичи джавы", "Придумать", ctx.project().id(), ctx.milestone().id(), team.manager().id())
.orElseThrow();
var t3 = services.ticketService()
.createTicket("Установить курсор", "И удалить", ctx.project().id(), ctx.milestone().id(), team.manager().id())
.orElseThrow();

services.ticketService().assignTicket(t1.id(), team.dev1().id(), team.manager().id());
services.ticketService().assignTicket(t2.id(), team.dev2().id(), team.manager().id());
services.ticketService().assignTicket(t3.id(), team.teamLead().id(), team.manager().id());
println("Created tickets");

completeTicket(services.ticketService(), t1.id(), team.dev1().id());
completeTicket(services.ticketService(), t2.id(), team.dev2().id());
completeTicket(services.ticketService(), t3.id(), team.teamLead().id());
println("All tickets are completed");

services.milestoneService().closeMilestone(ctx.milestone().id(), team.manager().id());
println("Milestone is closed");
}

void workWithBugs(Services services, Team team, ProjectContext ctx) {
printSection("Step 4. Working with bugs");

var bug1 = switch (services.bugReportService().createBugReport("NullPointer", "Everywhere", ctx.project().id(), team.tester().id())) {
case Result.Success(var b) -> { println("Bug created: " + b.title()); yield b; }
case Result.Failure(var e) -> throw new RuntimeException(e);
};

var bug2 = switch (services.bugReportService().createBugReport("UI is not working", "Why?", ctx.project().id(), team.tester().id())) {
case Result.Success(var b) -> { println("Bug created: " + b.title()); yield b; }
case Result.Failure(var e) -> throw new RuntimeException(e);
};

services.bugReportService().assignBug(bug1.id(), team.dev1().id(), team.manager().id());
services.bugReportService().assignBug(bug2.id(), team.dev2().id(), team.manager().id());
println("Bugs are assigned");

services.bugReportService().markFixed(bug1.id(), team.dev1().id());
services.bugReportService().markFixed(bug2.id(), team.dev2().id());
println("Bugs are fixed");

services.bugReportService().markTested(bug1.id(), team.tester().id());
println("Some bugs are tested");

services.bugReportService().closeBug(bug1.id(), team.tester().id());
println("Some bugs are closed");
}

void prepareReport(Services s, ProjectContext ctx) throws Exception {
printSection("Step 5. Preparing report");

try (var scope = StructuredTaskScope.open()) {

var ticketStatsTask = scope.fork(() -> {
Thread.sleep(100);
return s.ticketService().getTicketStats(ctx.milestone().id());
});

var bugStatsTask = scope.fork(() -> {
Thread.sleep(150);
return s.bugReportService().getBugStats(ctx.project().id());
});

var membersTask = scope.fork(() -> {
Thread.sleep(80);
return s.projectService().getProjectMembers(ctx.project().id());
});

scope.join();

var ticketStats = ticketStatsTask.get();
var bugStats = bugStatsTask.get();
var members = membersTask.get();

println(" Tickets: " + ticketStats);
println(" Bugs: " + bugStats);
println(" Participants: " + members.size());
}
}

void printSection(String text) {
println("\n▶ " + text);
println("─".repeat(45));
}

User registerUser(UserService userService, String name, String email, String label) {
return switch (userService.register(name, email)) {
case Result.Success(var user) -> { println("✓ " + label + ": " + user.name()); yield user; }
case Result.Failure(var e) -> throw new RuntimeException(e);
};
}

void completeTicket(TicketService ticketService, UUID id, UUID userId) {
ticketService.acceptTicket(id, userId);
ticketService.startWork(id, userId);
ticketService.completeTicket(id, userId);
}
4 changes: 0 additions & 4 deletions src/main/java/org/lab/Main.java

This file was deleted.

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

public sealed interface Result<T> {

record Success<T>(T value) implements Result<T> {}
record Failure<T>(String error) implements Result<T> {}

static <T> Result<T> success(T value) {
return new Success<>(value);
}

static <T> Result<T> failure(String error) {
return new Failure<>(error);
}

default boolean isSuccess() {
return this instanceof Success;
}

default boolean isFailure() {
return this instanceof Failure;
}

default T orElseThrow() {
return switch (this) {
case Success<T>(var v) -> v;
case Failure<T>(var e) -> throw new IllegalStateException(e);
};
}
}
53 changes: 53 additions & 0 deletions src/main/java/org/lab/domain/project/BugReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.lab.domain.project;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

public record BugReport(
UUID id,
String title,
String description,
UUID projectId,
UUID reporterId,
Optional<UUID> assigneeId,
BugStatus status,
LocalDateTime createdAt
) {
public BugReport {
Objects.requireNonNull(id);
Objects.requireNonNull(title);
Objects.requireNonNull(projectId);
Objects.requireNonNull(reporterId);
Objects.requireNonNull(assigneeId);
Objects.requireNonNull(status);
Objects.requireNonNull(createdAt);
if (title.isBlank()) {
throw new IllegalArgumentException("Title cannot be blank");
}

description = description == null ? "" : description;
}

public static BugReport create(String title, String description, UUID projectId, UUID reporterId) {
return new BugReport(UUID.randomUUID(), title, description, projectId, reporterId,
Optional.empty(), BugStatus.NEW, LocalDateTime.now());
}

public BugReport withAssignee(UUID assigneeId) {
return new BugReport(id, title, description, projectId, reporterId, Optional.of(assigneeId), status, createdAt);
}

public BugReport markFixed() {
return new BugReport(id, title, description, projectId, reporterId, assigneeId, BugStatus.FIXED, createdAt);
}

public BugReport markTested() {
return new BugReport(id, title, description, projectId, reporterId, assigneeId, BugStatus.TESTED, createdAt);
}

public BugReport close() {
return new BugReport(id, title, description, projectId, reporterId, assigneeId, BugStatus.CLOSED, createdAt);
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/lab/domain/project/BugStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.lab.domain.project;

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