forked from Alexey/DAS_2024_1
Merge pull request 'kalyshev_yan_lab_3' (#137) from kalyshev_yan_lab_3 into main
Reviewed-on: Alexey/DAS_2024_1#137
This commit is contained in:
commit
7bd8910fcb
48
kalyshev_yan_lab_3/README.md
Normal file
48
kalyshev_yan_lab_3/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Отчет. Лабораторная работа 3
|
||||
|
||||
## Описание
|
||||
|
||||
В рамках лабораторной работы № 3 были реализованы два сервиса (Java + Spring), предоставляющие CRUD-операции для сущностей.
|
||||
Модель данных следующая:
|
||||
|
||||
Сущность "Помещение" (сервис room):
|
||||
|
||||
1. идентификатор помещения
|
||||
2. название
|
||||
3. адрес
|
||||
|
||||
Сущность "Компьютер" (сервис computer):
|
||||
|
||||
1. идентификатор компьютера
|
||||
2. название
|
||||
3. описание
|
||||
4. идентификатор помещения
|
||||
|
||||
Компьютер с помещением связан как "один ко многим".
|
||||
Каждый из сервисов имеет API с пятью эндпоинтами. При этом в сервисе computer при запросе компьютера по id происходит дополнительный запрос в сервис room для получения информации о помещении, связанным с компьютером.
|
||||
Происходит это взаимодействие с помощью библиотеки OpenFeign, которая использует HttpClient.
|
||||
В качестве хранилища данных использовалась СУБД Postgres. У каждого сервиса своя база данных в поднятой СУБД.
|
||||
Для создания схемы БД была использована библиотека Flyway, применившая написанные миграции при старте приложения.
|
||||
Запросы к сервисам проксируют шлюз на основе Nginx. Для этого перед запуском nginx был описан конфигурационный файл nginx.conf,
|
||||
в котором были указаны прослушиваемый порт и название сервера (в блоке server), маршруты до микросервисов и параметры проксирования (в блоке location).
|
||||
Таким образом, с помощью Docker Compose были подняты сервисы:
|
||||
|
||||
- room
|
||||
- computer
|
||||
- postgres
|
||||
- nginx
|
||||
|
||||
## Как запустить
|
||||
|
||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
||||
|
||||
Установите и запустите Docker Engine или Docker Desktop.
|
||||
Перейдите в папку, содержащую файл docker-compose.yml через консоль.
|
||||
Выполните команду:
|
||||
`docker compose up --build`
|
||||
Далее можно осуществлять запросы к сервисам по адресу http://localhost/{location} , где часть пути location меняется в зависимости от сервиса и запроса.
|
||||
|
||||
## Видео-отчет
|
||||
|
||||
Работоспособность лабораторной работы можно оценить в следующем видео: https://zyzf.space/s/iWxb6b4EFQjPias.
|
||||
Демонстрация взаимодействия с системой производится с помощью утилиты httpie.
|
38
kalyshev_yan_lab_3/computer-service/.gitignore
vendored
Normal file
38
kalyshev_yan_lab_3/computer-service/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
21
kalyshev_yan_lab_3/computer-service/Dockerfile
Normal file
21
kalyshev_yan_lab_3/computer-service/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
128
kalyshev_yan_lab_3/computer-service/pom.xml
Normal file
128
kalyshev_yan_lab_3/computer-service/pom.xml
Normal file
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.computer</groupId>
|
||||
<artifactId>computer-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
|
||||
<spring-cloud.version>2023.0.3</spring-cloud.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Amapstruct.defaultComponentModel=spring</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
@ -0,0 +1,14 @@
|
||||
package ru.computer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
public class ComputerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ComputerApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package ru.computer.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.computer.domain.CreateComputerRequest;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
import ru.computer.domain.ComputerResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Validated
|
||||
@Tag(name = "computer", description = "API для управления компьютерами")
|
||||
public interface ComputerController {
|
||||
|
||||
@Operation(summary = "Получение всех компьютеров")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
})
|
||||
@GetMapping(value = "/api/v1/computer")
|
||||
List<ComputerEntity> getComputers();
|
||||
|
||||
@Operation(summary = "Создание компьютера")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Компьютер успешно создана"),
|
||||
})
|
||||
@PostMapping(value = "/api/v1/computer")
|
||||
ComputerEntity createComputer(@RequestBody @NotNull CreateComputerRequest request);
|
||||
|
||||
@Operation(summary = "Получение информации о компьютере по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
@ApiResponse(responseCode = "404", description = "Компьютер не найден")
|
||||
})
|
||||
@GetMapping(value = "/api/v1/computer/{computerId}")
|
||||
ComputerResponse getComputer(@PathVariable UUID computerId);
|
||||
|
||||
@Operation(summary = "Редактирование компьютера")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные компьютер успешно изменен"),
|
||||
@ApiResponse(responseCode = "404", description = "Компьютер не найден")
|
||||
})
|
||||
@PutMapping(value = "/api/v1/computer/{computerId}")
|
||||
ComputerEntity updateComputer(@PathVariable UUID computerId,
|
||||
@RequestBody @NotNull CreateComputerRequest request);
|
||||
|
||||
@Operation(summary = "Удалении компьютера по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Компьютер успешно удален"),
|
||||
@ApiResponse(responseCode = "404", description = "Компьютер не найден")
|
||||
})
|
||||
@DeleteMapping(value = "/api/v1/computer/{computerId}")
|
||||
void deleteComputer(@PathVariable UUID computerId);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package ru.computer.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.computer.domain.CreateComputerRequest;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
import ru.computer.domain.ComputerResponse;
|
||||
import ru.computer.service.ComputerService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ComputerControllerImpl implements ComputerController {
|
||||
|
||||
private final ComputerService computerService;
|
||||
|
||||
@Override
|
||||
public List<ComputerEntity> getComputers() {
|
||||
return computerService.getComputers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerEntity createComputer(CreateComputerRequest request) {
|
||||
return computerService.createComputer(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerResponse getComputer(UUID computerId) {
|
||||
return computerService.getComputer(computerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerEntity updateComputer(UUID computerId, CreateComputerRequest request) {
|
||||
return computerService.updateComputer(computerId, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteComputer(UUID computerId) {
|
||||
computerService.deleteComputer(computerId);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package ru.computer.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "computer")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ComputerEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", nullable = true)
|
||||
private String description;
|
||||
|
||||
@Column(name = "room_id", nullable = false)
|
||||
private UUID roomId;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.computer.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "Данные по вакансии")
|
||||
public class ComputerResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private RoomResponse room;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package ru.computer.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateComputerRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private UUID roomId;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package ru.computer.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class RoomResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.computer.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends RoomAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class RoomAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details,
|
||||
Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package ru.computer.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(RoomAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(RoomAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
15
kalyshev_yan_lab_3/computer-service/src/main/java/ru/computer/external/RoomClient.java
vendored
Normal file
15
kalyshev_yan_lab_3/computer-service/src/main/java/ru/computer/external/RoomClient.java
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package ru.computer.external;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import ru.computer.domain.RoomResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@FeignClient(name = "room", url = "${app.feign.room-url}")
|
||||
public interface RoomClient {
|
||||
|
||||
@GetMapping(value = "/api/v1/room/{roomId}")
|
||||
RoomResponse getRoom(@PathVariable UUID roomId);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.computer.mapper;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import ru.computer.domain.RoomResponse;
|
||||
import ru.computer.domain.CreateComputerRequest;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
import ru.computer.domain.ComputerResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper(imports = UUID.class)
|
||||
public interface ComputerMapper {
|
||||
|
||||
@Mapping(target = "room", source = "room")
|
||||
@Mapping(target = "name", source = "entity.name")
|
||||
@Mapping(target = "id", source = "entity.id")
|
||||
ComputerResponse map(ComputerEntity entity, RoomResponse room);
|
||||
|
||||
ComputerEntity map(CreateComputerRequest request);
|
||||
|
||||
ComputerEntity map(@MappingTarget ComputerEntity entity, CreateComputerRequest request);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package ru.computer.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface ComputerRepository extends JpaRepository<ComputerEntity, UUID> {
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.computer.service;
|
||||
|
||||
import ru.computer.domain.CreateComputerRequest;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
import ru.computer.domain.ComputerResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ComputerService {
|
||||
|
||||
List<ComputerEntity> getComputers();
|
||||
|
||||
ComputerEntity createComputer(CreateComputerRequest request);
|
||||
|
||||
ComputerEntity updateComputer(UUID computerId, CreateComputerRequest request);
|
||||
|
||||
void deleteComputer(UUID computerId);
|
||||
|
||||
ComputerResponse getComputer(UUID computerId);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package ru.computer.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.computer.domain.CreateComputerRequest;
|
||||
import ru.computer.domain.ComputerEntity;
|
||||
import ru.computer.domain.ComputerResponse;
|
||||
import ru.computer.mapper.ComputerMapper;
|
||||
import ru.computer.repository.ComputerRepository;
|
||||
import ru.computer.service.adapter.RoomAdapter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.computer.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ComputerServiceImpl implements ComputerService {
|
||||
|
||||
private final ComputerRepository computerRepository;
|
||||
private final RoomAdapter roomAdapter;
|
||||
private final ComputerMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<ComputerEntity> getComputers() {
|
||||
return computerRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerEntity createComputer(CreateComputerRequest request) {
|
||||
var entity = mapper.map(request);
|
||||
return computerRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerEntity updateComputer(UUID computerId, CreateComputerRequest request) {
|
||||
var entity = getById(computerId);
|
||||
entity = mapper.map(entity, request);
|
||||
|
||||
return computerRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteComputer(UUID computerId) {
|
||||
var entity = getById(computerId);
|
||||
computerRepository.delete(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputerResponse getComputer(UUID computerId) {
|
||||
var entity = getById(computerId);
|
||||
var room = roomAdapter.getRoomById(entity.getRoomId());
|
||||
|
||||
return mapper.map(entity, room);
|
||||
}
|
||||
|
||||
private ComputerEntity getById(UUID computerId) {
|
||||
var entity = computerRepository.findById(computerId);
|
||||
if (entity.isEmpty()) {
|
||||
log.warn("The computer with id '{}' was not found", computerId);
|
||||
throw notFound("Компьютер не найден");
|
||||
}
|
||||
return entity.get();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package ru.computer.service.adapter;
|
||||
|
||||
import ru.computer.domain.RoomResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface RoomAdapter {
|
||||
|
||||
RoomResponse getRoomById(UUID companyId);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.computer.service.adapter;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.computer.domain.RoomResponse;
|
||||
import ru.computer.external.RoomClient;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoomAdapterImpl implements RoomAdapter {
|
||||
|
||||
private final RoomClient roomClient;
|
||||
|
||||
@Override
|
||||
public RoomResponse getRoomById(UUID roomId) {
|
||||
return roomClient.getRoom(roomId);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: computer-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://localhost:5433/computer}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
||||
|
||||
app:
|
||||
feign:
|
||||
room-url: ${ROOM_URL:http://localhost:8081}
|
@ -0,0 +1,7 @@
|
||||
CREATE TABLE computer (
|
||||
id UUID
|
||||
CONSTRAINT computer_pk PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(1000),
|
||||
room_id UUID NOT NULL
|
||||
);
|
43
kalyshev_yan_lab_3/docker-compose.yml
Normal file
43
kalyshev_yan_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,43 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
container_name: postgres
|
||||
environment:
|
||||
POSTGRES_USERNAME: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
PGDATA: "/var/lib/postgresql/data/pgdata"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./postgres_data:/var/lib/postgresql/data/
|
||||
- ./init-database.sh:/docker-entrypoint-initdb.d/init-database.sh
|
||||
room:
|
||||
build: ./room-service
|
||||
container_name: room
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/room
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
computer:
|
||||
build: ./computer-service
|
||||
container_name: computer
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/computer
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
ROOM_URL: http://nginx/
|
||||
nginx:
|
||||
image: nginx
|
||||
depends_on:
|
||||
- computer
|
||||
- room
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- 80:80
|
8
kalyshev_yan_lab_3/init-database.sh
Normal file
8
kalyshev_yan_lab_3/init-database.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Создаем БД
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE DATABASE computer;
|
||||
CREATE DATABASE room;
|
||||
EOSQL
|
21
kalyshev_yan_lab_3/nginx/nginx.conf
Normal file
21
kalyshev_yan_lab_3/nginx/nginx.conf
Normal file
@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location /api/v1/room {
|
||||
proxy_pass http://room:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location /api/v1/computer {
|
||||
proxy_pass http://computer:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
38
kalyshev_yan_lab_3/room-service/.gitignore
vendored
Normal file
38
kalyshev_yan_lab_3/room-service/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
21
kalyshev_yan_lab_3/room-service/Dockerfile
Normal file
21
kalyshev_yan_lab_3/room-service/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
66
kalyshev_yan_lab_3/room-service/pom.xml
Normal file
66
kalyshev_yan_lab_3/room-service/pom.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.room</groupId>
|
||||
<artifactId>room-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
package ru.room;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class RoomApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RoomApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package ru.room.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.room.domain.RoomEntity;
|
||||
import ru.room.domain.CreateRoomRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Validated
|
||||
@Tag(name = "room", description = "API для управления компаниями")
|
||||
public interface RoomController {
|
||||
|
||||
@Operation(summary = "Получение всех компаний")
|
||||
@GetMapping(value = "/api/v1/room")
|
||||
List<RoomEntity> getCompanies();
|
||||
|
||||
@Operation(summary = "Создание компании")
|
||||
@PostMapping(value = "/api/v1/room")
|
||||
RoomEntity createRoom(@RequestBody @NotNull CreateRoomRequest roomRequest);
|
||||
|
||||
@Operation(summary = "Получение информации о компании по id")
|
||||
@GetMapping(value = "/api/v1/room/{roomId}")
|
||||
RoomEntity getRoom(@PathVariable UUID roomId);
|
||||
|
||||
@Operation(summary = "Редактирование компании")
|
||||
@PutMapping(value = "/api/v1/room/{roomId}")
|
||||
RoomEntity updateRoom(@PathVariable UUID roomId,
|
||||
@RequestBody @NotNull CreateRoomRequest roomRequest);
|
||||
|
||||
@Operation(summary = "Удалении компании по id")
|
||||
@DeleteMapping(value = "/api/v1/room/{roomId}")
|
||||
void deleteRoom(@PathVariable UUID roomId);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package ru.room.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.room.domain.RoomEntity;
|
||||
import ru.room.domain.CreateRoomRequest;
|
||||
import ru.room.service.RoomService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class RoomControllerImpl implements RoomController {
|
||||
|
||||
private final RoomService roomService;
|
||||
|
||||
@Override
|
||||
public List<RoomEntity> getCompanies() {
|
||||
return roomService.getCompanies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity createRoom(CreateRoomRequest roomRequest) {
|
||||
return roomService.createRoom(roomRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity getRoom(UUID roomId) {
|
||||
return roomService.getRoom(roomId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity updateRoom(UUID roomId, CreateRoomRequest roomRequest) {
|
||||
return roomService.updateRoom(roomId, roomRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRoom(UUID roomId) {
|
||||
roomService.deleteRoom(roomId);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.room.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateRoomRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package ru.room.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "room")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RoomEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "address", unique = true, nullable = false)
|
||||
private String address;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package ru.room.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.room.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends RoomAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class RoomAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public RoomAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details,
|
||||
Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package ru.room.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(RoomAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(RoomAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package ru.room.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.room.domain.RoomEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface RoomRepository extends JpaRepository<RoomEntity, UUID> {
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.room.service;
|
||||
|
||||
import ru.room.domain.RoomEntity;
|
||||
import ru.room.domain.CreateRoomRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface RoomService {
|
||||
|
||||
List<RoomEntity> getCompanies();
|
||||
|
||||
RoomEntity createRoom(CreateRoomRequest roomRequest);
|
||||
|
||||
RoomEntity updateRoom(UUID roomId, CreateRoomRequest roomRequest);
|
||||
|
||||
void deleteRoom(UUID roomId);
|
||||
|
||||
RoomEntity getRoom(UUID roomId);
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package ru.room.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.room.domain.RoomEntity;
|
||||
import ru.room.domain.CreateRoomRequest;
|
||||
import ru.room.repository.RoomRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.room.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoomServiceImpl implements RoomService {
|
||||
|
||||
private final RoomRepository roomRepository;
|
||||
|
||||
@Override
|
||||
public List<RoomEntity> getCompanies() {
|
||||
return roomRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity createRoom(CreateRoomRequest roomRequest) {
|
||||
var entity = RoomEntity.builder()
|
||||
.name(roomRequest.getName())
|
||||
.address(roomRequest.getAddress())
|
||||
.build();
|
||||
return roomRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity updateRoom(UUID roomId, CreateRoomRequest roomRequest) {
|
||||
var room = getById(roomId);
|
||||
room.setName(roomRequest.getName());
|
||||
room.setAddress(roomRequest.getAddress());
|
||||
return roomRepository.save(room);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRoom(UUID roomId) {
|
||||
var room = getById(roomId);
|
||||
roomRepository.delete(room);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomEntity getRoom(UUID roomId) {
|
||||
return getById(roomId);
|
||||
}
|
||||
|
||||
private RoomEntity getById(UUID roomId) {
|
||||
return roomRepository.findById(roomId)
|
||||
.orElseThrow(() -> notFound("Компания не найдена"));
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: room-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://postgres:5433/room}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
@ -0,0 +1,6 @@
|
||||
CREATE TABLE room (
|
||||
id UUID
|
||||
CONSTRAINT room_pk PRIMARY KEY,
|
||||
name VARCHAR(255) UNIQUE NOT NULL,
|
||||
address VARCHAR(255) NOT NULL
|
||||
);
|
Loading…
Reference in New Issue
Block a user