Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
9e5bac6
add deadline
github-classroom[bot] Nov 27, 2025
a378e02
initial
deadlovelll Nov 30, 2025
3698964
db client
deadlovelll Nov 30, 2025
c29eeb0
создан репозиторий сотрудников
deadlovelll Nov 30, 2025
ea8a912
dto file for user creation
deadlovelll Nov 30, 2025
17ee7e1
mod
deadlovelll Nov 30, 2025
e17071f
mapper
deadlovelll Nov 30, 2025
f9d9636
impl to Employee domain model
deadlovelll Nov 30, 2025
a229d63
impl on dto
deadlovelll Nov 30, 2025
ffd062a
fixed dto
deadlovelll Nov 30, 2025
9533785
adapter
deadlovelll Nov 30, 2025
dd3bf59
created use case for user creation, and validation
deadlovelll Nov 30, 2025
511b714
added exception handling
deadlovelll Nov 30, 2025
c46d140
employee create route done
deadlovelll Nov 30, 2025
a3a53a9
dg to 21, fixed config
deadlovelll Nov 30, 2025
4ffdd6b
added docker
deadlovelll Nov 30, 2025
011fccc
updated gradle and dockerfile
deadlovelll Nov 30, 2025
a19dc22
added object mapper implementation, added create method impl for empl…
deadlovelll Nov 30, 2025
b844331
delete method impl
deadlovelll Nov 30, 2025
45e8d54
added employee deletion
deadlovelll Nov 30, 2025
7dc7cff
get employee use_case
deadlovelll Dec 1, 2025
052cc2a
fix dto in out
deadlovelll Dec 1, 2025
8304257
added getById implementation in the employee repository
deadlovelll Dec 1, 2025
6903f4b
sealed app objects
deadlovelll Dec 2, 2025
d002902
direcotry for project domain
deadlovelll Dec 2, 2025
5f94be6
directories in application layer
deadlovelll Dec 2, 2025
3d827c8
test + dep inj
deadlovelll Dec 2, 2025
566fa39
project use case creationg
deadlovelll Dec 2, 2025
d8ca9f0
get and list adapters
deadlovelll Dec 2, 2025
1a3cf1a
use case for getting project info
deadlovelll Dec 2, 2025
ecc49d5
adapter for get uc
deadlovelll Dec 2, 2025
e48094f
added all usecases for project
deadlovelll Dec 3, 2025
1b01027
register routes
deadlovelll Dec 3, 2025
04e2b50
simplified the logic of project get
deadlovelll Dec 3, 2025
f27270b
repo methods
deadlovelll Dec 3, 2025
757e159
added cast
deadlovelll Dec 3, 2025
44c9467
tests for delete employee adapter
deadlovelll Dec 3, 2025
005a0f7
added test file for get adapter, added success test for deeltion
deadlovelll Dec 3, 2025
55d1edb
get success test
deadlovelll Dec 3, 2025
fbdd260
rm redundant dto
deadlovelll Dec 3, 2025
3c3fc1c
fail test for get
deadlovelll Dec 3, 2025
d9c3d4e
created method in repository
deadlovelll Dec 5, 2025
4a920d7
added test for creating employee
deadlovelll Dec 5, 2025
802d1c4
added exception handling in projectcreateadapter
deadlovelll Dec 5, 2025
5f8e286
added tests fro project create adapter
deadlovelll Dec 5, 2025
59b910d
tests for all project adapters
deadlovelll Dec 5, 2025
d4f9789
fixes after testing employee cruds
deadlovelll Dec 5, 2025
f279f47
added data extracting into project repository
deadlovelll Dec 5, 2025
3ce04e2
added data extractor to employee repository
deadlovelll Dec 6, 2025
34c7b03
fixes after testing with projects
deadlovelll Dec 6, 2025
8a89daf
fix imports
deadlovelll Dec 6, 2025
c06e286
updated specs
deadlovelll Dec 6, 2025
5b80197
ticket logic
deadlovelll Dec 6, 2025
eef1dfd
added methods for getting ticket
deadlovelll Dec 6, 2025
101611c
added di
deadlovelll Dec 6, 2025
a599f3f
bug fixes after testing
deadlovelll Dec 6, 2025
c501b6e
added functionality for closing ticket
deadlovelll Dec 6, 2025
1fb0c94
registered route for closing ticket
deadlovelll Dec 6, 2025
dcc07bd
completed logic with creating error message
deadlovelll Dec 6, 2025
ed5eee3
fixed imports
deadlovelll Dec 6, 2025
e1cd832
fixes after tests
deadlovelll Dec 6, 2025
e14313d
added endpoint for closing error message
deadlovelll Dec 6, 2025
afb0798
some tests for functionality
deadlovelll Dec 6, 2025
c41f315
tests for tickets
deadlovelll Dec 6, 2025
e940ab4
tests done
deadlovelll Dec 6, 2025
64d7c92
removed *
deadlovelll Dec 6, 2025
f34743e
rm redundant improts
deadlovelll Dec 6, 2025
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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.gradle
build
out
*.iml
.idea
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM eclipse-temurin:21-jdk AS build

WORKDIR /app

COPY build.gradle.kts settings.gradle.kts gradlew ./
COPY gradle ./gradle

RUN chmod +x gradlew

RUN ./gradlew build -x test --dry-run

COPY src ./src
RUN ./gradlew clean fatJar -x test

FROM eclipse-temurin:21-jre

WORKDIR /app

COPY --from=build /app/build/libs/*-all.jar app.jar

EXPOSE 8080

CMD ["java", "--enable-preview", "-jar", "app.jar"]
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
57 changes: 56 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
plugins {
id("java")
id("application")
}

application {
mainClass.set("org.lab.Main")
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}

group = "org.lab"
Expand All @@ -10,11 +21,55 @@ repositories {
}

dependencies {
implementation("io.javalin:javalin:6.1.3")
implementation("ch.qos.logback:logback-classic:1.4.11")
implementation("org.postgresql:postgresql:42.7.8")
implementation("com.zaxxer:HikariCP:5.0.1")
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
implementation("com.fasterxml.jackson.core:jackson-core:2.17.2")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.17.2")
implementation("com.google.inject:guice:5.1.0")
implementation("javax.inject:javax.inject:1")

compileOnly("org.projectlombok:lombok:1.18.34")
annotationProcessor("org.projectlombok:lombok:1.18.34")

testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-inline:5.2.0")
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testCompileOnly("org.projectlombok:lombok:1.18.34")
testAnnotationProcessor("org.projectlombok:lombok:1.18.34")
}

tasks.register<Jar>("fatJar") {
archiveClassifier.set("all")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes["Main-Class"] = "org.lab.Main"
}
from(sourceSets.main.get().output)

dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }
})
}

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

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

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

tasks.test {
useJUnitPlatform()
}
}

27 changes: 27 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
services:
postgres:
image: postgres:16
container_name: app-postgres
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data

app:
build: .
container_name: java-app
depends_on:
- postgres
ports:
- "7070:7070"
restart: always
volumes:
- "./app:/var/www"

volumes:
pgdata:
65 changes: 62 additions & 3 deletions src/main/java/org/lab/Main.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,63 @@
void main() {
IO.println("Hello and welcome!");
}
package org.lab;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
import io.javalin.Javalin;

import org.lab.api.adapters.employee.EmployeeCreateAdapter;
import org.lab.api.adapters.employee.EmployeeDeleteAdapter;
import org.lab.api.adapters.employee.EmployeeGetAdapter;
import org.lab.api.adapters.error_message.ErrorMessageCloseAdapter;
import org.lab.api.adapters.error_message.ErrorMessageCreateAdapter;
import org.lab.api.adapters.project.ProjectCreateAdapter;
import org.lab.api.adapters.project.ProjectDeleteAdapter;
import org.lab.api.adapters.project.ProjectGetAdapter;
import org.lab.api.adapters.project.ProjectListAdapter;
import org.lab.api.adapters.ticket.TicketCloseAdapter;
import org.lab.api.adapters.ticket.TicketCreateAdapter;
import org.lab.infra.di.AppModule;

public class Main {

public static void main(String[] args) {
Injector injector = Guice.createInjector(Stage.DEVELOPMENT, new AppModule());
Javalin app = Javalin.create(config -> {}).start(7070);

EmployeeCreateAdapter createEmployeeAdapter = injector.getInstance(EmployeeCreateAdapter.class);
EmployeeDeleteAdapter deleteEmployeeAdapter = injector.getInstance(EmployeeDeleteAdapter.class);
EmployeeGetAdapter getEmployeeAdapter = injector.getInstance(EmployeeGetAdapter.class);

ProjectCreateAdapter createProjectAdapter = injector.getInstance(ProjectCreateAdapter.class);
ProjectGetAdapter projectGetAdapter = injector.getInstance(ProjectGetAdapter.class);
ProjectDeleteAdapter projectDeleteAdapter = injector.getInstance(ProjectDeleteAdapter.class);
ProjectListAdapter projectListAdapter = injector.getInstance(ProjectListAdapter.class);

TicketCreateAdapter ticketCreateAdapter = injector.getInstance(TicketCreateAdapter.class);
TicketCloseAdapter ticketCloseAdapter = injector.getInstance(TicketCloseAdapter.class);

ErrorMessageCreateAdapter errorMessageCreateAdapter = injector.getInstance(
ErrorMessageCreateAdapter.class
);
ErrorMessageCloseAdapter errorMessageCloseAdapter = injector.getInstance(
ErrorMessageCloseAdapter.class
);

app.get("/", ctx -> ctx.result("Hello World"));

app.post("/employee/{actorId}", createEmployeeAdapter::createEmployee);
app.delete("/employee/{employeeId}/{actorId}", deleteEmployeeAdapter::deleteEmployee);
app.get("/employee/{employeeId}/{actorId}", getEmployeeAdapter::getEmployee);

app.get("/project/list/{employeeId}", projectListAdapter::listProjects);
app.post("/project/{employeeId}", createProjectAdapter::createProject);
app.get("/project/{projectId}/{employeeId}", projectGetAdapter::getProject);
app.delete("/project/{projectId}/{employeeId}", projectDeleteAdapter::deleteProject);

app.post("/ticket/{employeeId}/{projectId}", ticketCreateAdapter::createTicket);
app.patch("/ticket/{employeeId}/{ticketId}", ticketCloseAdapter::closeTicket);

app.post("/error_message/{employeeId}", errorMessageCreateAdapter::createErrorMessage);
app.patch("/error_message/{messageId}", errorMessageCloseAdapter::closeMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.lab.api.adapters.employee;

import java.util.Map;

import com.google.inject.Inject;
import io.javalin.http.Context;

import org.lab.application.employee.dto.GetEmployeeDTO;
import org.lab.application.employee.dto.CreateEmployeeDTO;
import org.lab.application.employee.use_cases.CreateEmployeeUseCase;
import org.lab.core.utils.mapper.ObjectMapper;
import org.lab.domain.emploee.model.Employee;
import org.lab.domain.shared.exceptions.NotPermittedException;

public class EmployeeCreateAdapter {

private final ObjectMapper mapper;
private final CreateEmployeeUseCase useCase;

@Inject
public EmployeeCreateAdapter(
ObjectMapper mapper,
CreateEmployeeUseCase useCase
) {
this.mapper = mapper;
this.useCase = useCase;
}

public Context createEmployee(
Context ctx
) {
try {
int actorId = Integer.parseInt(ctx.pathParam("actorId"));
CreateEmployeeDTO dto = ctx.bodyAsClass(CreateEmployeeDTO.class);
Employee employee = mapper.mapToDomain(dto, Employee.class);
Employee createdEmployee = useCase.execute(
employee,
actorId
);
GetEmployeeDTO presentationEmployee = mapper.mapToPresentation(
createdEmployee,
GetEmployeeDTO.class
);
return ctx.status(201).json(presentationEmployee);

} catch (NotPermittedException e) {
return ctx.status(403).json(
Map.of(
"error",
"You do not have permission to perform this operation"
)
);

} catch (Exception e) {
return ctx.status(500).json(Map.of("error", "Internal server error"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.lab.api.adapters.employee;

import java.util.Map;

import com.google.inject.Inject;
import io.javalin.http.Context;
import org.lab.application.employee.use_cases.DeleteEmployeeUseCase;
import org.lab.domain.shared.exceptions.NotPermittedException;

public class EmployeeDeleteAdapter {

private final DeleteEmployeeUseCase useCase;

@Inject
public EmployeeDeleteAdapter(
DeleteEmployeeUseCase useCase
) {
this.useCase = useCase;
}

public Context deleteEmployee(
Context ctx
) {
try {
int employeeId = Integer.parseInt(ctx.pathParam("employeeId"));
int actorId = Integer.parseInt(ctx.pathParam("actorId"));
this.useCase.execute(employeeId, actorId);
return ctx.status(201);

} catch (NotPermittedException e) {
return ctx.status(403).json(
Map.of(
"error",
"You do not have permission to perform this operation"
)
);

} catch (Exception e) {
return ctx.status(500).json(Map.of("error", "Internal server error"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.lab.api.adapters.employee;

import java.util.Map;

import com.google.inject.Inject;
import io.javalin.http.Context;

import org.lab.application.employee.dto.GetEmployeeDTO;
import org.lab.application.employee.use_cases.GetEmployeeUseCase;
import org.lab.core.utils.mapper.ObjectMapper;
import org.lab.domain.emploee.model.Employee;
import org.lab.domain.shared.exceptions.NotPermittedException;

public class EmployeeGetAdapter {

private final GetEmployeeUseCase useCase;
private final ObjectMapper mapper;

@Inject
public EmployeeGetAdapter(
GetEmployeeUseCase useCase,
ObjectMapper mapper
) {
this.useCase = useCase;
this.mapper = mapper;
}

public Context getEmployee(
Context ctx
) {
try {
int employeeId = Integer.parseInt(ctx.pathParam("employeeId"));
int actorId = Integer.parseInt(ctx.pathParam("actorId"));
Employee receivedEmployee = useCase.execute(
employeeId,
actorId
);
GetEmployeeDTO presentationEmployee = mapper.mapToPresentation(
receivedEmployee,
GetEmployeeDTO.class
);
return ctx.status(201).json(presentationEmployee);

} catch (NotPermittedException e) {
return ctx.status(403).json(
Map.of(
"error",
"You do not have permission to perform this operation"
)
);

} catch (Exception e) {
return ctx.status(500).json(Map.of("error", "Internal server error"));
}
}
}
Loading