Skip to content

Commit 994c388

Browse files
first commit
0 parents  commit 994c388

File tree

23 files changed

+1048
-0
lines changed

23 files changed

+1048
-0
lines changed

.github/workflows/ci.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Main CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Cache Maven packages
18+
uses: actions/cache@v3
19+
with:
20+
path: ~/.m2
21+
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
22+
restore-keys: |
23+
${{ runner.os }}-maven-
24+
25+
- name: Set up Docker Compose
26+
run: docker compose version
27+
28+
- name: Start services with Docker Compose
29+
run: docker compose up -d
30+
31+
- name: Wait for PostgreSQL
32+
run: |
33+
until docker exec $(docker ps -qf "name=db") pg_isready -U postgres; do
34+
echo "Waiting for PostgreSQL..."
35+
sleep 2
36+
done
37+
38+
- name: Set up OpenJDK 21
39+
uses: actions/setup-java@v4
40+
with:
41+
distribution: 'temurin'
42+
java-version: '21'
43+
44+
- name: Build and run tests
45+
run: mvn verify
46+
47+
- name: Upload JaCoCo report
48+
uses: actions/upload-artifact@v4
49+
with:
50+
name: jacoco-report
51+
path: target/site/jacoco
52+
53+
- name: Upload JaCoCo coverage to Qlty Cloud
54+
uses: qltysh/qlty-action/coverage@v1
55+
with:
56+
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
57+
files: target/site/jacoco/jacoco.xml
58+
59+
60+
- name: Run Checkstyle
61+
run: mvn checkstyle:check
62+
63+
- name: Tear down Docker Compose
64+
if: always()
65+
run: docker compose down

.gitignore

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Compiled class file
2+
*.class
3+
4+
# Log file
5+
*.log
6+
7+
# BlueJ files
8+
*.ctxt
9+
10+
# Mobile Tools for Java (J2ME)
11+
.mtj.tmp/
12+
13+
# Package Files #
14+
*.jar
15+
*.war
16+
*.nar
17+
*.ear
18+
*.zip
19+
*.tar.gz
20+
*.rar
21+
22+
# virtual machine crash logs
23+
hs_err_pid*
24+
replay_pid*
25+
26+
# IntelliJ IDEA
27+
.idea/
28+
*.iml
29+
*.iws
30+
*.ipr
31+
out/
32+
33+
# Maven
34+
target/
35+
pom.xml.tag
36+
pom.xml.releaseBackup
37+
pom.xml.versionsBackup
38+
pom.xml.next
39+
release.properties
40+
dependency-reduced-pom.xml
41+
buildNumber.properties
42+
.mvn/timing.properties
43+
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
44+
.mvn/wrapper/maven-wrapper.jar
45+
46+
# OS-specific
47+
.DS_Store
48+
Thumbs.db

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
![CI](https://github.com/irinakomarchenko/user-service-spring/actions/workflows/ci.yml/badge.svg)
2+
3+
[![Maintainability](https://qlty.sh/badges/a56c7491-b9be-4239-964a-541250c083e3/maintainability.svg)](https://qlty.sh/gh/irinakomarchenko/projects/user-service-spring)
4+
5+
# User Service Spring
6+
7+
**User Service Spring** — REST API-сервис на Spring Boot для управления пользователями (CRUD).
8+
9+
---
10+
11+
## Технологии
12+
13+
- Java 22
14+
- Spring Boot
15+
- Spring Web (REST API)
16+
- Spring Data JPA (встроенный Hibernate)
17+
- PostgreSQL (через Docker Compose)
18+
- SLF4J + Logback (логирование)
19+
- JUnit 5 + MockMvc (тестирование контроллеров и API)
20+
- Maven (сборка и зависимости)
21+
- Checkstyle (проверка стиля кода)
22+
- Lombok — (автогенерации геттеров/сеттеров)
23+
---
24+
25+
## Запуск проекта
26+
27+
### 1. Клонировать репозиторий
28+
29+
```sh
30+
git clone https://github.com/ТВОЙ_ЛОГИН/user-service-hibernate.git
31+
cd user-service-hibernate
32+
```
33+
### 2 Запустить базу данных PostgreSQL
34+
```sh
35+
docker compose up -d
36+
```
37+
### 3. Собрать проект и проверить стиль кода
38+
39+
```sh
40+
mvn clean install
41+
mvn checkstyle:check
42+
```
43+
### 4. Запустить тесты
44+
45+
```sh
46+
mvn test
47+
```
48+
### 5. Формирование отчета о тестах
49+
50+
```sh
51+
mvn verify
52+
```
53+
54+
### 6. Запустить приложение
55+
56+
57+
```sh
58+
mvn clean package
59+
java -jar target/user-service-spring.jar
60+
```
61+
62+
### 7. Пример работы приложения
63+
![img_1.png](readme-resources/img_1.png)

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: '3.8'
2+
services:
3+
db:
4+
image: postgres:14.1-alpine
5+
restart: always
6+
environment:
7+
- POSTGRES_USER=postgres
8+
- POSTGRES_PASSWORD=postgres
9+
ports:
10+
- '5432:5432'
11+
volumes:
12+
- db:/var/lib/postgresql/data
13+
volumes:
14+
db:
15+
driver: local

pom.xml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4+
https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.springframework.boot</groupId>
9+
<artifactId>spring-boot-starter-parent</artifactId>
10+
<version>3.4.6</version>
11+
<relativePath/>
12+
</parent>
13+
14+
<groupId>myuserservice</groupId>
15+
<artifactId>user-service-spring</artifactId>
16+
<version>1.0-SNAPSHOT</version>
17+
18+
<properties>
19+
<maven.compiler.source>21</maven.compiler.source>
20+
<maven.compiler.target>21</maven.compiler.target>
21+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
22+
</properties>
23+
<dependencies>
24+
<dependency>
25+
<groupId>org.springframework.boot</groupId>
26+
<artifactId>spring-boot-starter-web</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-validation</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-data-jpa</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.postgresql</groupId>
38+
<artifactId>postgresql</artifactId>
39+
<scope>runtime</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.projectlombok</groupId>
43+
<artifactId>lombok</artifactId>
44+
<scope>provided</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.springframework.boot</groupId>
48+
<artifactId>spring-boot-starter-test</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.testcontainers</groupId>
53+
<artifactId>junit-jupiter</artifactId>
54+
<version>1.19.7</version>
55+
<scope>test</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.testcontainers</groupId>
59+
<artifactId>postgresql</artifactId>
60+
<version>1.19.7</version>
61+
<scope>test</scope>
62+
</dependency>
63+
</dependencies>
64+
<build>
65+
<plugins>
66+
67+
<plugin>
68+
<groupId>org.jacoco</groupId>
69+
<artifactId>jacoco-maven-plugin</artifactId>
70+
<version>0.8.11</version>
71+
<executions>
72+
<execution>
73+
<id>prepare-agent</id>
74+
<goals>
75+
<goal>prepare-agent</goal>
76+
</goals>
77+
</execution>
78+
<execution>
79+
<id>report</id>
80+
<phase>verify</phase>
81+
<goals>
82+
<goal>report</goal>
83+
</goals>
84+
</execution>
85+
</executions>
86+
</plugin>
87+
88+
<plugin>
89+
<groupId>org.springframework.boot</groupId>
90+
<artifactId>spring-boot-maven-plugin</artifactId>
91+
</plugin>
92+
<plugin>
93+
<groupId>org.apache.maven.plugins</groupId>
94+
<artifactId>maven-checkstyle-plugin</artifactId>
95+
<version>3.3.1</version>
96+
<executions>
97+
<execution>
98+
<id>checkstyle-validation</id>
99+
<phase>verify</phase>
100+
<goals>
101+
<goal>check</goal>
102+
</goals>
103+
</execution>
104+
</executions>
105+
<configuration>
106+
<configLocation>google_checks.xml</configLocation>
107+
<consoleOutput>true</consoleOutput>
108+
<failsOnError>true</failsOnError>
109+
<includeTestSourceDirectory>true</includeTestSourceDirectory>
110+
<sourceDirectories>
111+
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
112+
<sourceDirectory>${project.basedir}/src/test/java</sourceDirectory>
113+
</sourceDirectories>
114+
</configuration>
115+
</plugin>
116+
</plugins>
117+
</build>
118+
</project>

readme-resources/img_1.png

67 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package myuserservice;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class UserServiceSpringApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(UserServiceSpringApplication.class, args);
11+
}
12+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package myuserservice.advice;
2+
3+
import jakarta.persistence.EntityNotFoundException;
4+
import org.springframework.dao.DataIntegrityViolationException;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.web.bind.MethodArgumentNotValidException;
8+
import org.springframework.web.bind.annotation.ExceptionHandler;
9+
import org.springframework.web.bind.annotation.RestControllerAdvice;
10+
11+
import java.util.stream.Collectors;
12+
13+
14+
@RestControllerAdvice
15+
public class GlobalExceptionHandler {
16+
17+
@ExceptionHandler(EntityNotFoundException.class)
18+
public ResponseEntity<String> handleNotFound(EntityNotFoundException ex) {
19+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
20+
}
21+
22+
@ExceptionHandler(DataIntegrityViolationException.class)
23+
public ResponseEntity<String> handleDuplicate(DataIntegrityViolationException ex) {
24+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Email already exists");
25+
}
26+
27+
@ExceptionHandler(MethodArgumentNotValidException.class)
28+
public ResponseEntity<String> handleValidation(MethodArgumentNotValidException ex) {
29+
String message = ex.getBindingResult().getFieldErrors().stream()
30+
.map(err -> err.getField() + ": " + err.getDefaultMessage())
31+
.collect(Collectors.joining(", "));
32+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Validation error: " + message);
33+
}
34+
35+
@ExceptionHandler(Exception.class)
36+
public ResponseEntity<String> handleGeneric(Exception ex) {
37+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
38+
.body("Unexpected error occurred");
39+
}
40+
}

0 commit comments

Comments
 (0)