├── .github └── workflows │ ├── api-tests.yml │ ├── dashboard-template.hbs │ └── wait-for-it.sh ├── .gitignore ├── README.md ├── checkstyle.xml ├── common ├── pom.xml └── src │ └── main │ └── java │ └── ru │ └── practicum │ └── ewm │ ├── Constants.java │ └── dto │ └── stats │ ├── StatInDto.java │ └── StatOutDto.java ├── docker-compose.yml ├── ewm-main-service-spec.json ├── ewm-stats-service-spec.json ├── lombok.config ├── main ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── ru │ │ │ └── practicum │ │ │ └── ewm │ │ │ ├── ErrorHandler.java │ │ │ ├── MainAPI.java │ │ │ ├── dto │ │ │ ├── categories │ │ │ │ ├── CategoryFullDto.java │ │ │ │ └── CategoryInDto.java │ │ │ ├── compilations │ │ │ │ ├── CompilationInDto.java │ │ │ │ └── CompilationOutDto.java │ │ │ ├── events │ │ │ │ ├── EventInDto.java │ │ │ │ ├── EventOutDto.java │ │ │ │ └── EventPublicOutDto.java │ │ │ ├── locations │ │ │ │ └── LocationDto.java │ │ │ ├── requests │ │ │ │ └── RequestOutDto.java │ │ │ └── users │ │ │ │ ├── UserDto.java │ │ │ │ └── UserPublicDto.java │ │ │ ├── endpoints │ │ │ ├── admin │ │ │ │ ├── AdminCategoriesController.java │ │ │ │ ├── AdminCompilationsController.java │ │ │ │ ├── AdminEventsController.java │ │ │ │ ├── AdminStatsController.java │ │ │ │ ├── AdminUsersController.java │ │ │ │ └── service │ │ │ │ │ ├── AdminCategoriesService.java │ │ │ │ │ ├── AdminCategoriesServiceImpl.java │ │ │ │ │ ├── AdminCompilationsService.java │ │ │ │ │ ├── AdminCompilationsServiceImpl.java │ │ │ │ │ ├── AdminEventsService.java │ │ │ │ │ ├── AdminEventsServiceImpl.java │ │ │ │ │ ├── AdminUsersService.java │ │ │ │ │ └── AdminUsersServiceImpl.java │ │ │ ├── pub │ │ │ │ ├── PublicCategoriesController.java │ │ │ │ ├── PublicCompilationsController.java │ │ │ │ ├── PublicEventsController.java │ │ │ │ └── service │ │ │ │ │ ├── CategoriesService.java │ │ │ │ │ ├── CategoriesServiceImpl.java │ │ │ │ │ ├── CompilationsService.java │ │ │ │ │ ├── CompilationsServiceImpl.java │ │ │ │ │ ├── EventsService.java │ │ │ │ │ └── EventsServiceImpl.java │ │ │ └── user │ │ │ │ ├── UsersEventsController.java │ │ │ │ ├── UsersEventsRequestsController.java │ │ │ │ ├── UsersRequestsController.java │ │ │ │ └── service │ │ │ │ ├── RequestNotFoundException.java │ │ │ │ ├── RequestsService.java │ │ │ │ ├── RequestsServiceImpl.java │ │ │ │ ├── UsersEventsRequestsService.java │ │ │ │ ├── UsersEventsRequestsServiceImpl.java │ │ │ │ ├── UsersEventsService.java │ │ │ │ └── UsersEventsServiceImpl.java │ │ │ ├── exception │ │ │ ├── CategoryNotFoundException.java │ │ │ ├── CompilationNotFoundException.java │ │ │ ├── DoubleLikeException.java │ │ │ ├── EventClosedException.java │ │ │ ├── EventNotFoundException.java │ │ │ ├── LikeNotFoundException.java │ │ │ ├── UserNotFoundException.java │ │ │ └── UserRequestHimselfException.java │ │ │ ├── mapper │ │ │ ├── CategoryMapper.java │ │ │ ├── CompilationMapper.java │ │ │ ├── EventMapper.java │ │ │ ├── LocationMapper.java │ │ │ ├── RequestMapper.java │ │ │ └── UserMapper.java │ │ │ ├── model │ │ │ ├── Category.java │ │ │ ├── Compilation.java │ │ │ ├── Event.java │ │ │ ├── EventState.java │ │ │ ├── Like.java │ │ │ ├── LikeType.java │ │ │ ├── Location.java │ │ │ ├── Request.java │ │ │ ├── RequestState.java │ │ │ ├── SortType.java │ │ │ └── User.java │ │ │ ├── repository │ │ │ ├── CategoriesRepository.java │ │ │ ├── CompilationsRepository.java │ │ │ ├── EventsRepository.java │ │ │ ├── LikeRepository.java │ │ │ ├── RequestsRepository.java │ │ │ └── UsersRepository.java │ │ │ └── utils │ │ │ ├── AdminStatsClient.java │ │ │ ├── BaseClient.java │ │ │ └── Utils.java │ └── resources │ │ ├── application.properties │ │ └── schema.sql │ └── test │ └── java │ └── ru │ └── practicum │ └── ewm │ ├── MainAPITest.java │ ├── UtilsTest.java │ └── model │ └── dto │ ├── categories │ ├── CategoryFullDtoTest.java │ └── CategoryInDtoTest.java │ ├── compilations │ ├── CompilationInDtoTest.java │ └── CompilationOutDtoTest.java │ ├── events │ ├── EventInDtoTest.java │ └── EventOutDtoTest.java │ ├── locations │ └── LocationDtoTest.java │ ├── requests │ └── RequestOutDtoTest.java │ └── users │ └── UserDtoTest.java ├── pom.xml ├── postman ├── ewm-like-service.json ├── ewm-main-service.json └── ewm-stat-service.json ├── stats ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── ru │ │ │ └── practicum │ │ │ └── ewm │ │ │ ├── ErrorHandler.java │ │ │ ├── StatsAPI.java │ │ │ ├── controller │ │ │ └── StatsController.java │ │ │ ├── mapper │ │ │ └── StatMapper.java │ │ │ ├── model │ │ │ └── Stat.java │ │ │ ├── repository │ │ │ └── StatsRepository.java │ │ │ └── service │ │ │ ├── StatsService.java │ │ │ └── StatsServiceImpl.java │ └── resources │ │ ├── application.properties │ │ └── schema.sql │ └── test │ └── java │ └── ru │ └── practicum │ └── ewm │ ├── StatsAPITest.java │ ├── dto │ └── stats │ │ ├── StatInDtoTest.java │ │ └── StatOutDtoTest.java │ └── mapper │ └── StatMapperTest.java └── uml ├── db-ewm-image.png ├── db-ewm.puml └── project.puml /.github/workflows/api-tests.yml: -------------------------------------------------------------------------------- 1 | name: Explore With Me API Tests 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | uses: yandex-praktikum/java-explore-with-me/.github/workflows/api-tests.yml@ci -------------------------------------------------------------------------------- /.github/workflows/wait-for-it.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Use this script to test if a given TCP host/port are available 3 | 4 | WAITFORIT_cmdname=${0##*/} 5 | 6 | echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } 7 | 8 | usage() 9 | { 10 | cat << USAGE >&2 11 | Usage: 12 | $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] 13 | -h HOST | --host=HOST Host or IP under test 14 | -p PORT | --port=PORT TCP port under test 15 | Alternatively, you specify the host and port as host:port 16 | -s | --strict Only execute subcommand if the test succeeds 17 | -q | --quiet Don't output any status messages 18 | -t TIMEOUT | --timeout=TIMEOUT 19 | Timeout in seconds, zero for no timeout 20 | -- COMMAND ARGS Execute command with args after the test finishes 21 | USAGE 22 | exit 1 23 | } 24 | 25 | wait_for() 26 | { 27 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 28 | echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 29 | else 30 | echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" 31 | fi 32 | WAITFORIT_start_ts=$(date +%s) 33 | while : 34 | do 35 | if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then 36 | nc -z $WAITFORIT_HOST $WAITFORIT_PORT 37 | WAITFORIT_result=$? 38 | else 39 | # (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 40 | (curl --fail --silent $WAITFORIT_HOST:$WAITFORIT_PORT/actuator/health | grep UP) >/dev/null 2>&1 41 | WAITFORIT_result=$? 42 | fi 43 | if [[ $WAITFORIT_result -eq 0 ]]; then 44 | WAITFORIT_end_ts=$(date +%s) 45 | echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" 46 | break 47 | fi 48 | sleep 1 49 | done 50 | return $WAITFORIT_result 51 | } 52 | 53 | wait_for_wrapper() 54 | { 55 | # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 56 | if [[ $WAITFORIT_QUIET -eq 1 ]]; then 57 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 58 | else 59 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 60 | fi 61 | WAITFORIT_PID=$! 62 | trap "kill -INT -$WAITFORIT_PID" INT 63 | wait $WAITFORIT_PID 64 | WAITFORIT_RESULT=$? 65 | if [[ $WAITFORIT_RESULT -ne 0 ]]; then 66 | echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 67 | fi 68 | return $WAITFORIT_RESULT 69 | } 70 | 71 | # process arguments 72 | while [[ $# -gt 0 ]] 73 | do 74 | case "$1" in 75 | *:* ) 76 | WAITFORIT_hostport=(${1//:/ }) 77 | WAITFORIT_HOST=${WAITFORIT_hostport[0]} 78 | WAITFORIT_PORT=${WAITFORIT_hostport[1]} 79 | shift 1 80 | ;; 81 | --child) 82 | WAITFORIT_CHILD=1 83 | shift 1 84 | ;; 85 | -q | --quiet) 86 | WAITFORIT_QUIET=1 87 | shift 1 88 | ;; 89 | -s | --strict) 90 | WAITFORIT_STRICT=1 91 | shift 1 92 | ;; 93 | -h) 94 | WAITFORIT_HOST="$2" 95 | if [[ $WAITFORIT_HOST == "" ]]; then break; fi 96 | shift 2 97 | ;; 98 | --host=*) 99 | WAITFORIT_HOST="${1#*=}" 100 | shift 1 101 | ;; 102 | -p) 103 | WAITFORIT_PORT="$2" 104 | if [[ $WAITFORIT_PORT == "" ]]; then break; fi 105 | shift 2 106 | ;; 107 | --port=*) 108 | WAITFORIT_PORT="${1#*=}" 109 | shift 1 110 | ;; 111 | -t) 112 | WAITFORIT_TIMEOUT="$2" 113 | if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi 114 | shift 2 115 | ;; 116 | --timeout=*) 117 | WAITFORIT_TIMEOUT="${1#*=}" 118 | shift 1 119 | ;; 120 | --) 121 | shift 122 | WAITFORIT_CLI=("$@") 123 | break 124 | ;; 125 | --help) 126 | usage 127 | ;; 128 | *) 129 | echoerr "Unknown argument: $1" 130 | usage 131 | ;; 132 | esac 133 | done 134 | 135 | if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then 136 | echoerr "Error: you need to provide a host and port to test." 137 | usage 138 | fi 139 | 140 | WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} 141 | WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} 142 | WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} 143 | WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} 144 | 145 | # Check to see if timeout is from busybox? 146 | WAITFORIT_TIMEOUT_PATH=$(type -p timeout) 147 | WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) 148 | 149 | WAITFORIT_BUSYTIMEFLAG="" 150 | if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then 151 | WAITFORIT_ISBUSY=1 152 | # Check if busybox timeout uses -t flag 153 | # (recent Alpine versions don't support -t anymore) 154 | if timeout &>/dev/stdout | grep -q -e '-t '; then 155 | WAITFORIT_BUSYTIMEFLAG="-t" 156 | fi 157 | else 158 | WAITFORIT_ISBUSY=0 159 | fi 160 | 161 | if [[ $WAITFORIT_CHILD -gt 0 ]]; then 162 | wait_for 163 | WAITFORIT_RESULT=$? 164 | exit $WAITFORIT_RESULT 165 | else 166 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 167 | wait_for_wrapper 168 | WAITFORIT_RESULT=$? 169 | else 170 | wait_for 171 | WAITFORIT_RESULT=$? 172 | fi 173 | fi 174 | 175 | if [[ $WAITFORIT_CLI != "" ]]; then 176 | if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then 177 | echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" 178 | exit $WAITFORIT_RESULT 179 | fi 180 | exec "${WAITFORIT_CLI[@]}" 181 | else 182 | exit $WAITFORIT_RESULT 183 | fi -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | /1 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _REST API для проекта "Explore With Me"_ 2 | 3 | ### _Язык реализации Java_ 4 | 5 | В рамках дипломного проекта разработано REST API для приложения ExploreWithMe (англ. «исследуй со мной»). Оно 6 | предоставляет 7 | возможность 8 | делиться информацией об интересных событиях и помогать найти компанию для участия в них. 9 | 10 | ### **Инструкция по развертыванию проекта:** 11 | 12 | 1. [Скачать данный репозиторий](https://github.com/bannovdaniil/java-explore-with-me) 13 | 2. mvn clean package 14 | 3. mvn install 15 | 4. docker-compose build 16 | 5. docker-compose up -d 17 | 6. основной сервис доступен по адресу: http://localhost:8080 18 | 7. сервис статистики доступен по адресу: http://localhost:9090 19 | 20 | ### _Приложение включает в себя сервисы:_ 21 | 22 | - GateWay (разрабатывается отдельно) 23 | - Проверяет права пользователей 24 | - Передает запросы на остальные микросервисы в зависимости прав 25 | - Основной сервис — содержит всё необходимое для работы. 26 | - Просмотр событий без авторизации; 27 | - Возможность создания и управления категориями; 28 | - События и работа с ними - создание, модерация; 29 | - Запросы пользователей на участие в событии - запрос, подтверждение, отклонение. 30 | - Создание и управление подборками. 31 | - Добавление и удаление Лайков событиям, формирование рейтингов. 32 | - Сервис статистики — хранит количество просмотров и позволяет делать различные выборки для анализа работы приложения. 33 | - Отдельный сервис для сбора статистики; 34 | 35 | ### _Описание сервисов:_ 36 | 37 | #### _Основной сервис, выделенный порт: 8080_ 38 | 39 | - **Публичный** (_доступен для всех пользователей_) 40 | - API для работы с событиями 41 | - API для работы с категориями 42 | - **Приватный** (_доступен только для зарегистрированных пользователей_) 43 | - API для работы с событиями 44 | - API для работы с запросами текущего пользователя на участие в событиях 45 | - API для работы с рейтингами 46 | - **Административный** (_доступен только для администратора проекта_) 47 | - API для работы с событиями 48 | - API для работы с категориями 49 | - API для работы с пользователями 50 | - API для работы с подборками событий 51 | 52 | #### _Сервис статистики, выделенный порт: 9090_ 53 | 54 | - **Административный** (_доступен только для администратора проекта_) 55 | - API для работы со статистикой посещений 56 | 57 | #### _Фича Рейтинги включена в Основной сервис_ 58 | 59 | - Сортировка событий по рейтингу 60 | - Оценивать можно только Опубликованные события 61 | - Изменить рейтинг могут только пользователи с подтвержденным участием 62 | - При изменении рейтига события изменяется, рейтинг создателя события. 63 | - При публичном просмотре событий, скрыты данные создателя события. 64 | - Создатель события не может оценивать событие. 65 | 66 | _**Список эндпоинтов для фичи:_** 67 | 68 | - Поставить лайк/дизлайк: 69 | - PUT ewm-main:8080/users/{userId}/events/{eventId}/like?type={like/dislike} 70 | - Убрать лайк/дизлайк: 71 | - DELETE ewm-main:8080/users/{userId}/events/{eventId}/like?type={like/dislike} 72 | 73 | ### _Спецификация REST API swagger_ 74 | 75 | - [Основной сервис](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/ewm-main-service-spec.json) 76 | - [Сервис статистики](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/ewm-stats-service-spec.json) 77 | 78 | ### _Postman тесты для сервисов:_ 79 | 80 | - [Основной сервис](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/postman/ewm-main-service.json) 81 | - [Сервис статистики](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/postman/ewm-stat-service.json) 82 | - [Тест для фичи Рейтинги](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/postman/ewm-like-service.json) 83 | 84 | ### _Схема Архитектуры проекта_ 85 | 86 | ![Class Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/uml/project.puml?new) 87 | 88 | ### _Схема Базы данных проекта_ 89 | 90 | ![Class Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/uml/db-ewm.puml?new) 91 | 92 | ![](https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/develop/uml/db-ewm-image.png) 93 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | explore-with-me 9 | ru.practicum 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | common 14 | 0.0.1-SNAPSHOT 15 | 16 | 17 | 11 18 | 11 19 | UTF-8 20 | 21 | 22 | 23 | org.projectlombok 24 | lombok 25 | true 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-validation 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /common/src/main/java/ru/practicum/ewm/Constants.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import java.time.format.DateTimeFormatter; 4 | 5 | public class Constants { 6 | public static final String DATE_TIME_STRING = "yyyy-MM-dd HH:mm:ss"; 7 | public static final DateTimeFormatter DATE_TIME_SPACE = DateTimeFormatter.ofPattern(DATE_TIME_STRING); 8 | public static final String PAGE_SIZE_STRING = "10"; 9 | public static final int USER_TIME_HOUR_BEFORE_START = 2; 10 | public static final int ADMIN_TIME_HOUR_BEFORE_START = 1; 11 | } -------------------------------------------------------------------------------- /common/src/main/java/ru/practicum/ewm/dto/stats/StatInDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.stats; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import ru.practicum.ewm.Constants; 9 | 10 | import javax.validation.constraints.NotNull; 11 | import java.time.LocalDateTime; 12 | 13 | @Getter 14 | @Setter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class StatInDto { 18 | @NotNull 19 | private String app; 20 | @NotNull 21 | private String uri; 22 | @NotNull 23 | private String ip; 24 | @NotNull 25 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 26 | private LocalDateTime timestamp; 27 | 28 | public StatInDto(String app, String uri, String ip, String timestamp) { 29 | this.app = app; 30 | this.uri = uri; 31 | this.ip = ip; 32 | this.timestamp = LocalDateTime.parse(timestamp, Constants.DATE_TIME_SPACE); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/ru/practicum/ewm/dto/stats/StatOutDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.stats; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @AllArgsConstructor 10 | public class StatOutDto { 11 | private String app; 12 | private String uri; 13 | private Long hits; 14 | } 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | # Stats 4 | stats-server: 5 | build: ./stats 6 | container_name: stats_server 7 | restart: always 8 | ports: 9 | - "9090:9090" 10 | depends_on: 11 | stats-db: 12 | condition: service_healthy 13 | environment: 14 | - WAIT_HOSTS=stats-db:5432 15 | - SPRING_DATASOURCE_URL=jdbc:postgresql://stats-db:5432/stats 16 | - POSTGRES_USER=sa 17 | - POSTGRES_PASSWORD=password 18 | 19 | # база данных для Stats 20 | stats-db: 21 | image: postgres:14.5-alpine 22 | container_name: stats_db 23 | restart: always 24 | volumes: 25 | - /var/lib/postgresql/data/ 26 | ports: 27 | - "6590:5432" 28 | # переменные окружения 29 | environment: 30 | POSTGRES_DB: stats 31 | POSTGRES_USER: sa 32 | POSTGRES_PASSWORD: password 33 | healthcheck: 34 | test: [ "CMD-SHELL", "pg_isready -U postgres" ] 35 | interval: 10s 36 | timeout: 5s 37 | retries: 5 38 | # Main 39 | ewm-service: 40 | build: ./main 41 | container_name: main_server 42 | restart: always 43 | ports: 44 | - "8080:8080" 45 | depends_on: 46 | ewm-db: 47 | condition: service_healthy 48 | 49 | environment: 50 | - WAIT_HOSTS=ewm-db:5432 51 | - SPRING_DATASOURCE_URL=jdbc:postgresql://ewm-db:5432/ewm 52 | - POSTGRES_USER=sa 53 | - POSTGRES_PASSWORD=password 54 | - STATS_SERVER_URL=http://stats-server:9090 55 | 56 | # база для MAIn 57 | ewm-db: 58 | image: postgres:14.5-alpine 59 | container_name: ewm_db 60 | restart: always 61 | volumes: 62 | - /var/lib/postgresql/data/ 63 | ports: 64 | - "6580:5432" 65 | # переменные окружения 66 | environment: 67 | POSTGRES_DB: ewm 68 | POSTGRES_USER: sa 69 | POSTGRES_PASSWORD: password 70 | healthcheck: 71 | test: [ "CMD-SHELL", "pg_isready -U postgres" ] 72 | interval: 10s 73 | timeout: 5s 74 | retries: 5 75 | -------------------------------------------------------------------------------- /ewm-stats-service-spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "Stat service API", 5 | "version": "v0" 6 | }, 7 | "servers": [ 8 | { 9 | "url": "http://localhost:9090", 10 | "description": "Generated server url" 11 | } 12 | ], 13 | "tags": [ 14 | { 15 | "name": "StatsController", 16 | "description": "API для работы со статистикой посещений" 17 | } 18 | ], 19 | "paths": { 20 | "/hit": { 21 | "post": { 22 | "tags": [ 23 | "StatsController" 24 | ], 25 | "summary": "Сохранение информации о том, что к эндпоинту был запрос", 26 | "description": "Сохранение информации о том, что на uri конкретного сервиса был отправлен запрос пользователем. Название сервиса, uri и ip пользователя указаны в теле запроса.", 27 | "operationId": "hit", 28 | "requestBody": { 29 | "description": "данные запроса", 30 | "content": { 31 | "application/json": { 32 | "schema": { 33 | "$ref": "#/components/schemas/EndpointHit" 34 | } 35 | } 36 | }, 37 | "required": true 38 | }, 39 | "responses": { 40 | "200": { 41 | "description": "Информация сохранена" 42 | } 43 | } 44 | } 45 | }, 46 | "/stats": { 47 | "get": { 48 | "tags": [ 49 | "StatsController" 50 | ], 51 | "summary": "Получение статистики по посещениям. Обратите внимание: значение даты и времени нужно закодировать (например используя java.net.URLEncoder.encode) ", 52 | "operationId": "getStats", 53 | "parameters": [ 54 | { 55 | "name": "start", 56 | "in": "query", 57 | "description": "Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")", 58 | "required": true, 59 | "schema": { 60 | "type": "string" 61 | } 62 | }, 63 | { 64 | "name": "end", 65 | "in": "query", 66 | "description": "Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")", 67 | "required": true, 68 | "schema": { 69 | "type": "string" 70 | } 71 | }, 72 | { 73 | "name": "uris", 74 | "in": "query", 75 | "description": "Список uri для которых нужно выгрузить статистику", 76 | "required": false, 77 | "schema": { 78 | "type": "array", 79 | "items": { 80 | "type": "string" 81 | } 82 | } 83 | }, 84 | { 85 | "name": "unique", 86 | "in": "query", 87 | "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)", 88 | "required": false, 89 | "schema": { 90 | "type": "boolean", 91 | "default": false 92 | } 93 | } 94 | ], 95 | "responses": { 96 | "200": { 97 | "description": "Статистика собрана", 98 | "content": { 99 | "application/json": { 100 | "schema": { 101 | "type": "array", 102 | "items": { 103 | "$ref": "#/components/schemas/ViewStats" 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | }, 113 | "components": { 114 | "schemas": { 115 | "EndpointHit": { 116 | "type": "object", 117 | "properties": { 118 | "id": { 119 | "type": "integer", 120 | "description": "Идентификатор записи", 121 | "format": "int64" 122 | }, 123 | "app": { 124 | "type": "string", 125 | "description": "Идентификатор сервиса для которого записывается информация" 126 | }, 127 | "uri": { 128 | "type": "string", 129 | "description": "URI для которого был осуществлен запрос " 130 | }, 131 | "ip": { 132 | "type": "string", 133 | "description": "IP-адрес пользователя, осуществившего запрос" 134 | }, 135 | "timestamp": { 136 | "type": "string", 137 | "description": "Дата и время, когда был совершен запрос к эндпоинту (в формате \"yyyy-MM-dd HH:mm:ss\")", 138 | "example": "2022-09-06 11:00:23" 139 | } 140 | } 141 | }, 142 | "ViewStats": { 143 | "type": "object", 144 | "properties": { 145 | "app": { 146 | "type": "string", 147 | "description": "Название сервиса" 148 | }, 149 | "uri": { 150 | "type": "string", 151 | "description": "URI сервиса" 152 | }, 153 | "hits": { 154 | "type": "integer", 155 | "description": "Количество просмотров", 156 | "format": "int64" 157 | } 158 | } 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.anyconstructor.addconstructorproperties = false 3 | lombok.addLombokGeneratedAnnotation = true 4 | lombok.addSuppressWarnings = false -------------------------------------------------------------------------------- /main/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:11-alpine-jdk 2 | COPY target/ewm-main-server*.jar ewm_main_server.jar 3 | ENTRYPOINT ["java","-jar","/ewm_main_server.jar"] 4 | -------------------------------------------------------------------------------- /main/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | explore-with-me 7 | ru.practicum 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | ewm-main-server 13 | 14 | 15 | 11 16 | 11 17 | UTF-8 18 | 19 | 20 | 21 | 22 | ru.practicum 23 | common 24 | 0.0.1-SNAPSHOT 25 | 26 | 27 | org.apache.httpcomponents 28 | httpclient 29 | 4.5.13 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-jpa 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-actuator 44 | 45 | 46 | 47 | org.postgresql 48 | postgresql 49 | 42.5.0 50 | runtime 51 | 52 | 53 | 54 | com.h2database 55 | h2 56 | runtime 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-configuration-processor 62 | true 63 | 64 | 65 | 66 | org.projectlombok 67 | lombok 68 | true 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-test 74 | test 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-test-autoconfigure 79 | 80 | 81 | org.springframework 82 | spring-test 83 | 84 | 85 | org.hamcrest 86 | hamcrest 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-autoconfigure 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.dao.DataIntegrityViolationException; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.web.bind.MethodArgumentNotValidException; 11 | import org.springframework.web.bind.MissingServletRequestParameterException; 12 | import org.springframework.web.bind.annotation.ExceptionHandler; 13 | import org.springframework.web.bind.annotation.ResponseStatus; 14 | import org.springframework.web.bind.annotation.RestControllerAdvice; 15 | import ru.practicum.ewm.endpoints.admin.AdminCategoriesController; 16 | import ru.practicum.ewm.endpoints.admin.AdminCompilationsController; 17 | import ru.practicum.ewm.endpoints.admin.AdminEventsController; 18 | import ru.practicum.ewm.endpoints.admin.AdminUsersController; 19 | import ru.practicum.ewm.endpoints.pub.PublicCategoriesController; 20 | import ru.practicum.ewm.endpoints.pub.PublicCompilationsController; 21 | import ru.practicum.ewm.endpoints.pub.PublicEventsController; 22 | import ru.practicum.ewm.endpoints.user.UsersEventsController; 23 | import ru.practicum.ewm.endpoints.user.UsersEventsRequestsController; 24 | import ru.practicum.ewm.endpoints.user.UsersRequestsController; 25 | import ru.practicum.ewm.endpoints.user.service.RequestNotFoundException; 26 | import ru.practicum.ewm.exception.*; 27 | 28 | import java.nio.file.AccessDeniedException; 29 | import java.security.InvalidParameterException; 30 | import java.time.LocalDateTime; 31 | 32 | @RestControllerAdvice( 33 | assignableTypes = { 34 | AdminUsersController.class, 35 | AdminCategoriesController.class, 36 | AdminCompilationsController.class, 37 | AdminEventsController.class, 38 | UsersEventsController.class, 39 | UsersRequestsController.class, 40 | UsersEventsRequestsController.class, 41 | PublicCategoriesController.class, 42 | PublicCompilationsController.class, 43 | PublicEventsController.class 44 | }) 45 | @Slf4j 46 | public class ErrorHandler { 47 | private ErrorResponse errorResponse; 48 | 49 | @ExceptionHandler({ 50 | CategoryNotFoundException.class, 51 | EventNotFoundException.class, 52 | UserNotFoundException.class, 53 | RequestNotFoundException.class, 54 | CompilationNotFoundException.class, 55 | LikeNotFoundException.class 56 | }) 57 | @ResponseStatus(HttpStatus.NOT_FOUND) 58 | public ErrorResponse handleNotFound(final Exception e) { 59 | log.info("Error handleNotFound: {}", e.getMessage()); 60 | return new ErrorResponse(e.getMessage()); 61 | } 62 | 63 | @ExceptionHandler({ 64 | DataIntegrityViolationException.class, 65 | MethodArgumentNotValidException.class, 66 | InvalidParameterException.class, 67 | MissingServletRequestParameterException.class 68 | }) 69 | @ResponseStatus(HttpStatus.BAD_REQUEST) 70 | public ErrorMessage handleBadRequest(final Exception e) { 71 | log.info("Error handleBadRequest: {}: {}", e.getClass().getSimpleName(), e.getMessage()); 72 | return new ErrorMessage( 73 | e.getClass().getSimpleName(), 74 | e.getMessage(), 75 | "", 76 | "BAD_REQUEST", 77 | Constants.DATE_TIME_SPACE.format(LocalDateTime.now())); 78 | } 79 | 80 | @ExceptionHandler({ 81 | IllegalArgumentException.class, 82 | IllegalStateException.class, 83 | UserRequestHimselfException.class 84 | }) 85 | @ResponseStatus(HttpStatus.BAD_REQUEST) 86 | public ErrorResponse handleArgumentBadRequest(final Exception e) { 87 | log.info("Error handleArgumentBadRequest: {}", e.getMessage()); 88 | return new ErrorResponse(e.getMessage()); 89 | } 90 | 91 | @ExceptionHandler({ 92 | EventClosedException.class, 93 | DoubleLikeException.class 94 | }) 95 | @ResponseStatus(HttpStatus.CONFLICT) 96 | public ErrorResponse handleDoubleData(final Exception e) { 97 | log.info("Error handleDoubleData: {}", e.getMessage()); 98 | return new ErrorResponse(e.getMessage()); 99 | } 100 | 101 | @ExceptionHandler({ 102 | AccessDeniedException.class 103 | }) 104 | @ResponseStatus(HttpStatus.FORBIDDEN) 105 | public ErrorResponse handleForbidden(final Exception e) { 106 | log.info("Error handleForbidden: {}", e.getMessage()); 107 | return new ErrorResponse(e.getMessage()); 108 | } 109 | 110 | @ExceptionHandler 111 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 112 | public ErrorResponse handleAllError(final Throwable e) { 113 | log.info("Error handleAllError: {}", e.getMessage()); 114 | return new ErrorResponse("Произошла непредвиденная ошибка. " + e.getMessage()); 115 | } 116 | 117 | private static class ErrorResponse { 118 | private final String error; 119 | 120 | public ErrorResponse(String error) { 121 | this.error = error; 122 | } 123 | 124 | public String getError() { 125 | return error; 126 | } 127 | } 128 | 129 | @AllArgsConstructor 130 | @Getter 131 | @Setter 132 | @ToString 133 | private static class ErrorMessage { 134 | private String errors; 135 | private String message; 136 | private String reason; 137 | private String status; 138 | private String timestamp; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/MainAPI.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MainAPI { 8 | public static final String APP_NAME = "ewm-main"; 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(MainAPI.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/categories/CategoryFullDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.categories; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | import javax.validation.constraints.Positive; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class CategoryFullDto { 16 | @Positive 17 | private Long id; 18 | @NotBlank 19 | private String name; 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/categories/CategoryInDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.categories; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class CategoryInDto { 15 | @NotBlank 16 | private String name; 17 | } 18 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/compilations/CompilationInDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.compilations; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | import java.util.List; 10 | 11 | @Setter 12 | @Getter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class CompilationInDto { 16 | @NotBlank 17 | private String title; 18 | private List events; 19 | private Boolean pinned; 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/compilations/CompilationOutDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.compilations; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import ru.practicum.ewm.dto.events.EventOutDto; 8 | 9 | import java.util.List; 10 | 11 | @Setter 12 | @Getter 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class CompilationOutDto { 16 | private Long id; 17 | private String title; 18 | private List events; 19 | private Boolean pinned; 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/events/EventInDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.events; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import ru.practicum.ewm.Constants; 9 | import ru.practicum.ewm.dto.locations.LocationDto; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Positive; 14 | import javax.validation.constraints.Size; 15 | import java.time.LocalDateTime; 16 | 17 | @Getter 18 | @Setter 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | public class EventInDto { 22 | private Long eventId; 23 | @NotBlank 24 | @Size(min = 20, max = 2000) 25 | private String annotation; 26 | @Positive 27 | private Long category; 28 | @NotBlank 29 | @Size(min = 20, max = 7000) 30 | private String description; 31 | private LocationDto location; 32 | @NotBlank 33 | @Size(min = 3, max = 120) 34 | private String title; 35 | @NotNull 36 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 37 | private LocalDateTime eventDate; 38 | private Boolean paid; 39 | @Positive 40 | private Integer participantLimit; 41 | private Boolean requestModeration; 42 | 43 | public EventInDto(String annotation, Long category, String description, LocationDto location, String title, String eventDate, Boolean paid, Integer participantLimit, Boolean requestModeration) { 44 | this.annotation = annotation; 45 | this.category = category; 46 | this.description = description; 47 | this.location = location; 48 | this.title = title; 49 | this.eventDate = LocalDateTime.parse(eventDate, Constants.DATE_TIME_SPACE); 50 | this.paid = paid; 51 | this.participantLimit = participantLimit; 52 | this.requestModeration = requestModeration; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/events/EventOutDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.events; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.*; 5 | import ru.practicum.ewm.Constants; 6 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 7 | import ru.practicum.ewm.dto.locations.LocationDto; 8 | import ru.practicum.ewm.dto.users.UserDto; 9 | import ru.practicum.ewm.model.EventState; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | public class EventOutDto { 19 | private String annotation; 20 | private CategoryFullDto category; 21 | private UserDto initiator; 22 | private LocationDto location; 23 | private String title; 24 | private Integer confirmedRequests; 25 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 26 | private LocalDateTime createdOn; 27 | private String description; 28 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 29 | private LocalDateTime eventDate; 30 | private Long id; 31 | private Boolean paid; 32 | private Integer participantLimit; 33 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 34 | private LocalDateTime publishedOn; 35 | private Boolean requestModeration; 36 | private EventState state; 37 | private Long views; 38 | private Integer rate; 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/events/EventPublicOutDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.events; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.*; 5 | import ru.practicum.ewm.Constants; 6 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 7 | import ru.practicum.ewm.dto.locations.LocationDto; 8 | import ru.practicum.ewm.dto.users.UserPublicDto; 9 | import ru.practicum.ewm.model.EventState; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | public class EventPublicOutDto { 19 | private String annotation; 20 | private CategoryFullDto category; 21 | private UserPublicDto initiator; 22 | private LocationDto location; 23 | private String title; 24 | private Integer confirmedRequests; 25 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 26 | private LocalDateTime createdOn; 27 | private String description; 28 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 29 | private LocalDateTime eventDate; 30 | private Long id; 31 | private Boolean paid; 32 | private Integer participantLimit; 33 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 34 | private LocalDateTime publishedOn; 35 | private Boolean requestModeration; 36 | private EventState state; 37 | private Long views; 38 | private Integer rate; 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/locations/LocationDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.locations; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class LocationDto { 13 | private float lat; 14 | private float lon; 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/requests/RequestOutDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.requests; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import ru.practicum.ewm.Constants; 9 | import ru.practicum.ewm.model.RequestState; 10 | 11 | import javax.validation.constraints.Positive; 12 | import java.time.LocalDateTime; 13 | 14 | @Getter 15 | @Setter 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class RequestOutDto { 19 | private Long id; 20 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 21 | private LocalDateTime created; 22 | @Positive 23 | private Long event; 24 | @Positive 25 | private Long requester; 26 | private RequestState status; 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/users/UserDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.users; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.validation.constraints.Email; 9 | 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class UserDto { 15 | private Long id; 16 | private String name; 17 | @Email 18 | private String email; 19 | private Float rate; 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/dto/users/UserPublicDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.users; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserPublicDto { 13 | private String name; 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/AdminCategoriesController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 8 | import ru.practicum.ewm.dto.categories.CategoryInDto; 9 | import ru.practicum.ewm.endpoints.admin.service.AdminCategoriesService; 10 | import ru.practicum.ewm.exception.CategoryNotFoundException; 11 | 12 | import javax.validation.Valid; 13 | import javax.validation.constraints.Positive; 14 | import java.nio.file.AccessDeniedException; 15 | 16 | @RestController 17 | @RequestMapping("/admin/categories") 18 | @RequiredArgsConstructor 19 | @Validated 20 | @Slf4j 21 | public class AdminCategoriesController { 22 | private final AdminCategoriesService adminCategoriesService; 23 | 24 | @PatchMapping 25 | public CategoryFullDto updateCategory(@Valid @RequestBody CategoryFullDto categoryFullDto) throws CategoryNotFoundException { 26 | log.info("Admin updateCategory: {}", categoryFullDto); 27 | return adminCategoriesService.updateCategory(categoryFullDto); 28 | } 29 | 30 | @PostMapping 31 | public CategoryFullDto addCategory(@Valid @RequestBody CategoryInDto categoryInDto) { 32 | log.info("Admin addCategory: {}", categoryInDto); 33 | return adminCategoriesService.addCategory(categoryInDto); 34 | } 35 | 36 | @DeleteMapping("{catId}") 37 | void removeCategory(@Positive @PathVariable Long catId) throws CategoryNotFoundException, AccessDeniedException { 38 | log.info("Admin removeCategory: {}", catId); 39 | adminCategoriesService.removeCategory(catId); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/AdminCompilationsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.dto.compilations.CompilationInDto; 8 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 9 | import ru.practicum.ewm.endpoints.admin.service.AdminCompilationsService; 10 | import ru.practicum.ewm.exception.CompilationNotFoundException; 11 | import ru.practicum.ewm.exception.EventNotFoundException; 12 | 13 | import javax.validation.Valid; 14 | import javax.validation.constraints.Positive; 15 | 16 | @RestController 17 | @RequestMapping("/admin/compilations") 18 | @RequiredArgsConstructor 19 | @Validated 20 | @Slf4j 21 | public class AdminCompilationsController { 22 | private final AdminCompilationsService adminCompilationsService; 23 | 24 | @PostMapping 25 | public CompilationOutDto addCompilation(@Valid @RequestBody CompilationInDto compilationInDto) { 26 | log.info("Admin addCompilation: {}", compilationInDto); 27 | return adminCompilationsService.addCompilation(compilationInDto); 28 | } 29 | 30 | @DeleteMapping("{compId}") 31 | public void removeCompilation(@Positive @PathVariable Long compId) throws CompilationNotFoundException { 32 | log.info("Admin removeCompilation: {}", compId); 33 | adminCompilationsService.removeCompilation(compId); 34 | } 35 | 36 | @DeleteMapping("{compId}/events/{eventId}") 37 | public void removeEventFromCompilation( 38 | @Positive @PathVariable Long compId, 39 | @Positive @PathVariable Long eventId 40 | ) throws CompilationNotFoundException { 41 | log.info("Admin removeEventFromCompilation: {},{}", compId, eventId); 42 | adminCompilationsService.removeEventFromCompilation(compId, eventId); 43 | } 44 | 45 | @PatchMapping("{compId}/events/{eventId}") 46 | public CompilationOutDto addEventToCompilation( 47 | @Positive @PathVariable Long compId, 48 | @Positive @PathVariable Long eventId 49 | ) throws CompilationNotFoundException, EventNotFoundException { 50 | log.info("Admin addEventToCompilation: {},{}", compId, eventId); 51 | return adminCompilationsService.addEventToCompilation(compId, eventId); 52 | } 53 | 54 | @DeleteMapping("{compId}/pin") 55 | public void unPinCompilation( 56 | @Positive @PathVariable Long compId 57 | ) throws CompilationNotFoundException { 58 | log.info("Admin unPinCompilation: {}", compId); 59 | adminCompilationsService.unPinCompilation(compId); 60 | } 61 | 62 | @PatchMapping("{compId}/pin") 63 | public void pinCompilation( 64 | @Positive @PathVariable Long compId 65 | ) throws CompilationNotFoundException { 66 | log.info("Admin pinCompilation: {}", compId); 67 | adminCompilationsService.pinCompilation(compId); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/AdminEventsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.events.EventInDto; 9 | import ru.practicum.ewm.dto.events.EventOutDto; 10 | import ru.practicum.ewm.endpoints.admin.service.AdminEventsService; 11 | import ru.practicum.ewm.exception.CategoryNotFoundException; 12 | import ru.practicum.ewm.exception.EventClosedException; 13 | import ru.practicum.ewm.exception.EventNotFoundException; 14 | import ru.practicum.ewm.exception.UserNotFoundException; 15 | 16 | import javax.validation.Valid; 17 | import javax.validation.constraints.Positive; 18 | import javax.validation.constraints.PositiveOrZero; 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequestMapping("/admin/events") 23 | @RequiredArgsConstructor 24 | @Validated 25 | @Slf4j 26 | public class AdminEventsController { 27 | private final AdminEventsService adminEventsService; 28 | 29 | @GetMapping 30 | public List findAllEvents( 31 | @RequestParam(value = "users", defaultValue = "") Long[] users, 32 | @RequestParam(value = "states", required = false) String[] states, 33 | @RequestParam(value = "categories", required = false) Long[] categories, 34 | @RequestParam(name = "rangeStart", required = false) String rangeStart, 35 | @RequestParam(name = "rangeEnd", required = false) String rangeEnd, 36 | @PositiveOrZero 37 | @RequestParam(name = "from", defaultValue = "0") Integer from, 38 | @Positive 39 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size) 40 | throws UserNotFoundException, CategoryNotFoundException { 41 | log.info("Admin findAllEvents: {},{},{},{},{},{},{}", 42 | users, states, categories, rangeStart, rangeEnd, from, size); 43 | return adminEventsService.findAllEvents(users, states, categories, rangeStart, rangeEnd, from, size); 44 | } 45 | 46 | @PatchMapping("{eventId}/publish") 47 | public EventOutDto publishEvent(@Positive @PathVariable Long eventId) 48 | throws EventNotFoundException, EventClosedException { 49 | log.info("Admin publishEvent: {}", eventId); 50 | return adminEventsService.publishEvent(eventId); 51 | } 52 | 53 | @PatchMapping("{eventId}/reject") 54 | public EventOutDto rejectEvent(@Positive @PathVariable Long eventId) 55 | throws EventNotFoundException, EventClosedException { 56 | log.info("Admin Patch rejectEvent: {}", eventId); 57 | return adminEventsService.rejectEvent(eventId); 58 | } 59 | 60 | @PutMapping("{eventId}") 61 | public EventOutDto updateEvent(@Positive @PathVariable Long eventId, 62 | @Valid @RequestBody EventInDto eventInDto) 63 | throws EventNotFoundException, CategoryNotFoundException { 64 | log.info("Admin Put updateEvent: {},{}", eventId, eventInDto); 65 | return adminEventsService.updateEvent(eventId, eventInDto); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/AdminStatsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.validation.annotation.Validated; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import ru.practicum.ewm.utils.AdminStatsClient; 11 | 12 | import javax.validation.Valid; 13 | import javax.validation.constraints.NotNull; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequiredArgsConstructor 18 | @Validated 19 | @Slf4j 20 | public class AdminStatsController { 21 | private final AdminStatsClient adminStatsClient; 22 | 23 | @GetMapping("/admin/stats") 24 | public ResponseEntity getHits(@NotNull @RequestParam(name = "start") String start, 25 | @NotNull @RequestParam(name = "end") String end, 26 | @Valid 27 | @RequestParam(name = "uris", defaultValue = "", required = false) List uris, 28 | @RequestParam(name = "unique", defaultValue = "false") Boolean unique) { 29 | log.info("getHits: {},{},{},{}", start, end, uris, unique); 30 | return adminStatsClient.getHits(start, end, uris, unique); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/AdminUsersController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.users.UserDto; 9 | import ru.practicum.ewm.endpoints.admin.service.AdminUsersService; 10 | import ru.practicum.ewm.exception.UserNotFoundException; 11 | 12 | import javax.validation.Valid; 13 | import javax.validation.constraints.Positive; 14 | import javax.validation.constraints.PositiveOrZero; 15 | import java.util.List; 16 | 17 | @RestController 18 | @RequestMapping("/admin/users") 19 | @RequiredArgsConstructor 20 | @Validated 21 | @Slf4j 22 | public class AdminUsersController { 23 | private final AdminUsersService adminUsersService; 24 | 25 | @PostMapping() 26 | public UserDto createUser(@Valid @RequestBody UserDto userDto) { 27 | log.info("createUser: {}", userDto); 28 | return adminUsersService.createUser(userDto); 29 | } 30 | 31 | @GetMapping 32 | public List findAll( 33 | @RequestParam(value = "ids") Long[] ids, 34 | @PositiveOrZero 35 | @RequestParam(defaultValue = "0") Integer from, 36 | @Positive 37 | @RequestParam(defaultValue = Constants.PAGE_SIZE_STRING) Integer size) { 38 | log.info("findAll: {},{}", ids, from); 39 | return adminUsersService.findAll(ids, from, size); 40 | } 41 | 42 | @DeleteMapping("{userId}") 43 | public void deleteUser(@Valid @Positive @PathVariable Long userId) throws UserNotFoundException { 44 | log.info("deleteUser: {}", userId); 45 | adminUsersService.deleteUserById(userId); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminCategoriesService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 4 | import ru.practicum.ewm.dto.categories.CategoryInDto; 5 | import ru.practicum.ewm.exception.CategoryNotFoundException; 6 | 7 | import java.nio.file.AccessDeniedException; 8 | 9 | public interface AdminCategoriesService { 10 | CategoryFullDto updateCategory(CategoryFullDto categoryFullDto) throws CategoryNotFoundException; 11 | 12 | CategoryFullDto addCategory(CategoryInDto categoryInDto); 13 | 14 | void removeCategory(Long catId) throws CategoryNotFoundException, AccessDeniedException; 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminCategoriesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 7 | import ru.practicum.ewm.dto.categories.CategoryInDto; 8 | import ru.practicum.ewm.exception.CategoryNotFoundException; 9 | import ru.practicum.ewm.mapper.CategoryMapper; 10 | import ru.practicum.ewm.model.Category; 11 | import ru.practicum.ewm.repository.CategoriesRepository; 12 | import ru.practicum.ewm.repository.EventsRepository; 13 | 14 | import java.nio.file.AccessDeniedException; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class AdminCategoriesServiceImpl implements AdminCategoriesService { 19 | private final CategoriesRepository categoriesRepository; 20 | private final EventsRepository eventsRepository; 21 | 22 | @Override 23 | public CategoryFullDto updateCategory(CategoryFullDto categoryFullDto) throws CategoryNotFoundException { 24 | checkCategoryExists(categoryFullDto.getId()); 25 | Category category = CategoryMapper.dtoOutToCategory(categoryFullDto); 26 | return CategoryMapper.categoryToDtoOut(categoriesRepository.save(category)); 27 | } 28 | 29 | @Override 30 | public CategoryFullDto addCategory(CategoryInDto categoryInDto) { 31 | Category category = CategoryMapper.dtoInToCategory(categoryInDto); 32 | return CategoryMapper.categoryToDtoOut(categoriesRepository.save(category)); 33 | } 34 | 35 | @Override 36 | @Transactional 37 | public void removeCategory(Long catId) throws CategoryNotFoundException, AccessDeniedException { 38 | checkCategoryExists(catId); 39 | if (eventsRepository.existsByCategoryId(catId)) { 40 | throw new AccessDeniedException("Category in use."); 41 | } 42 | categoriesRepository.deleteById(catId); 43 | } 44 | 45 | private void checkCategoryExists(Long catId) throws CategoryNotFoundException { 46 | if (!categoriesRepository.existsById(catId)) { 47 | throw new CategoryNotFoundException("Category ID was not found."); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminCompilationsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import ru.practicum.ewm.dto.compilations.CompilationInDto; 4 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 5 | import ru.practicum.ewm.exception.CompilationNotFoundException; 6 | import ru.practicum.ewm.exception.EventNotFoundException; 7 | 8 | public interface AdminCompilationsService { 9 | 10 | CompilationOutDto addCompilation(CompilationInDto compilationInDto); 11 | 12 | void removeCompilation(Long compId) throws CompilationNotFoundException; 13 | 14 | void removeEventFromCompilation(Long compId, Long eventId) throws CompilationNotFoundException; 15 | 16 | CompilationOutDto addEventToCompilation(Long compId, Long eventId) throws CompilationNotFoundException, EventNotFoundException; 17 | 18 | void pinCompilation(Long compId) throws CompilationNotFoundException; 19 | 20 | void unPinCompilation(Long compId) throws CompilationNotFoundException; 21 | } 22 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminCompilationsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | import ru.practicum.ewm.dto.compilations.CompilationInDto; 7 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 8 | import ru.practicum.ewm.exception.CompilationNotFoundException; 9 | import ru.practicum.ewm.exception.EventNotFoundException; 10 | import ru.practicum.ewm.mapper.CompilationMapper; 11 | import ru.practicum.ewm.model.Compilation; 12 | import ru.practicum.ewm.model.Event; 13 | import ru.practicum.ewm.repository.CompilationsRepository; 14 | import ru.practicum.ewm.repository.EventsRepository; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class AdminCompilationsServiceImpl implements AdminCompilationsService { 19 | private final EventsRepository eventsRepository; 20 | private final CompilationsRepository compilationsRepository; 21 | 22 | @Override 23 | @Transactional 24 | public CompilationOutDto addCompilation(CompilationInDto compilationInDto) { 25 | Compilation compilation = CompilationMapper.dtoToCompilation( 26 | compilationInDto, eventsRepository.findAllById(compilationInDto.getEvents()) 27 | ); 28 | 29 | return CompilationMapper.compilationToOutDto(compilationsRepository.saveAndFlush(compilation)); 30 | } 31 | 32 | @Override 33 | public void removeCompilation(Long compId) throws CompilationNotFoundException { 34 | if (!compilationsRepository.existsById(compId)) { 35 | throw new CompilationNotFoundException("Compilation ID not found."); 36 | } 37 | compilationsRepository.deleteById(compId); 38 | compilationsRepository.flush(); 39 | } 40 | 41 | @Override 42 | @Transactional 43 | public void removeEventFromCompilation(Long compId, Long eventId) throws CompilationNotFoundException { 44 | Compilation compilation = compilationsRepository.findById(compId).orElseThrow( 45 | () -> new CompilationNotFoundException("Compilation ID not found.") 46 | ); 47 | compilation.getEvents().removeIf((e) -> e.getId().equals(eventId)); 48 | compilationsRepository.flush(); 49 | } 50 | 51 | @Override 52 | public CompilationOutDto addEventToCompilation(Long compId, Long eventId) throws CompilationNotFoundException, EventNotFoundException { 53 | Compilation compilation = compilationsRepository.findById(compId).orElseThrow( 54 | () -> new CompilationNotFoundException("Compilation ID not found.") 55 | ); 56 | Event event = eventsRepository.findById(eventId).orElseThrow( 57 | () -> new EventNotFoundException("Event ID: " + eventId + " not found.") 58 | ); 59 | compilation.getEvents().add(event); 60 | compilationsRepository.flush(); 61 | 62 | return CompilationMapper.compilationToOutDto(compilation); 63 | } 64 | 65 | @Override 66 | public void pinCompilation(Long compId) throws CompilationNotFoundException { 67 | setPin(compId, true); 68 | } 69 | 70 | 71 | @Override 72 | public void unPinCompilation(Long compId) throws CompilationNotFoundException { 73 | setPin(compId, false); 74 | } 75 | 76 | private void setPin(Long compId, boolean pinned) throws CompilationNotFoundException { 77 | Compilation compilation = compilationsRepository.findById(compId).orElseThrow( 78 | () -> new CompilationNotFoundException("Compilation ID not found.") 79 | ); 80 | compilation.setPinned(pinned); 81 | compilationsRepository.flush(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminEventsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import ru.practicum.ewm.dto.events.EventInDto; 4 | import ru.practicum.ewm.dto.events.EventOutDto; 5 | import ru.practicum.ewm.exception.CategoryNotFoundException; 6 | import ru.practicum.ewm.exception.EventClosedException; 7 | import ru.practicum.ewm.exception.EventNotFoundException; 8 | import ru.practicum.ewm.exception.UserNotFoundException; 9 | 10 | import java.util.List; 11 | 12 | public interface AdminEventsService { 13 | List findAllEvents(Long[] users, String[] states, Long[] categories, String rangeStart, String rangeEnd, Integer from, Integer size) throws UserNotFoundException, CategoryNotFoundException; 14 | 15 | EventOutDto publishEvent(Long eventId) throws EventClosedException, EventNotFoundException; 16 | 17 | EventOutDto rejectEvent(Long eventId) throws EventClosedException, EventNotFoundException; 18 | 19 | EventOutDto updateEvent(Long eventId, EventInDto eventInDto) throws EventNotFoundException, CategoryNotFoundException; 20 | } 21 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminEventsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | import ru.practicum.ewm.Constants; 10 | import ru.practicum.ewm.dto.events.EventInDto; 11 | import ru.practicum.ewm.dto.events.EventOutDto; 12 | import ru.practicum.ewm.exception.CategoryNotFoundException; 13 | import ru.practicum.ewm.exception.EventClosedException; 14 | import ru.practicum.ewm.exception.EventNotFoundException; 15 | import ru.practicum.ewm.exception.UserNotFoundException; 16 | import ru.practicum.ewm.mapper.EventMapper; 17 | import ru.practicum.ewm.model.Event; 18 | import ru.practicum.ewm.model.EventState; 19 | import ru.practicum.ewm.repository.CategoriesRepository; 20 | import ru.practicum.ewm.repository.EventsRepository; 21 | import ru.practicum.ewm.repository.UsersRepository; 22 | import ru.practicum.ewm.utils.Utils; 23 | 24 | import java.time.LocalDateTime; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | @Service 29 | @RequiredArgsConstructor 30 | public class AdminEventsServiceImpl implements AdminEventsService { 31 | private final EventsRepository eventsRepository; 32 | private final UsersRepository usersRepository; 33 | private final CategoriesRepository categoriesRepository; 34 | 35 | @Override 36 | public List findAllEvents(Long[] users, String[] states, Long[] categories, String rangeStart, String rangeEnd, Integer from, Integer size) throws UserNotFoundException, CategoryNotFoundException { 37 | checkUsersExitOrThrow(users); 38 | checkCategoriesExitOrThrow(categories); 39 | List stateList; 40 | if (states != null) { 41 | stateList = checkStatesCorrectOrThrow(states); 42 | } else { 43 | stateList = List.of(); 44 | } 45 | LocalDateTime startDate; 46 | if (rangeStart != null) { 47 | startDate = LocalDateTime.parse(rangeStart, Constants.DATE_TIME_SPACE); 48 | } else { 49 | startDate = LocalDateTime.now(); 50 | } 51 | LocalDateTime endDate; 52 | if (rangeStart != null) { 53 | endDate = LocalDateTime.parse(rangeEnd, Constants.DATE_TIME_SPACE); 54 | } else { 55 | endDate = LocalDateTime.now(); 56 | } 57 | 58 | Sort sort = Sort.sort(Event.class).by(Event::getEventDate).descending(); 59 | Pageable pageable = PageRequest.of(from / size, size, sort); 60 | List eventList = eventsRepository.findAllByUsersAndStatesAndCategories(users, stateList, categories, startDate, endDate, pageable); 61 | return EventMapper.eventToListOutDto(eventList); 62 | } 63 | 64 | private List checkStatesCorrectOrThrow(String[] states) { 65 | List stateList = new ArrayList<>(); 66 | for (String state : states) { 67 | try { 68 | stateList.add(EventState.valueOf(state)); 69 | } catch (IllegalArgumentException err) { 70 | throw new IllegalArgumentException("Stats: " + state + " not found."); 71 | } 72 | } 73 | return stateList; 74 | } 75 | 76 | private void checkCategoriesExitOrThrow(Long[] categories) throws CategoryNotFoundException { 77 | for (Long catId : categories) { 78 | if (!categoriesRepository.existsById(catId)) { 79 | throw new CategoryNotFoundException("Category ID: " + catId + " not found."); 80 | } 81 | } 82 | } 83 | 84 | private void checkUsersExitOrThrow(Long[] users) throws UserNotFoundException { 85 | for (Long userId : users) { 86 | if (!usersRepository.existsById(userId)) { 87 | throw new UserNotFoundException("User ID: " + userId + " not found."); 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public EventOutDto publishEvent(Long eventId) throws EventClosedException, EventNotFoundException { 94 | Event event = eventsRepository.findById(eventId).orElseThrow( 95 | () -> new EventNotFoundException("Event ID not found.") 96 | ); 97 | if (event.getState() != EventState.PENDING) { 98 | throw new EventClosedException("Event is not pending."); 99 | } 100 | Utils.checkTimeBeforeOrThrow(event.getEventDate(), Constants.ADMIN_TIME_HOUR_BEFORE_START); 101 | event.setPublishedOn(LocalDateTime.now()); 102 | event.setState(EventState.PUBLISHED); 103 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 104 | } 105 | 106 | @Override 107 | public EventOutDto rejectEvent(Long eventId) throws EventClosedException, EventNotFoundException { 108 | Event event = eventsRepository.findById(eventId).orElseThrow( 109 | () -> new EventNotFoundException("Event ID not found.") 110 | ); 111 | if (event.getState() != EventState.PENDING) { 112 | throw new EventClosedException("Event is not pending."); 113 | } 114 | event.setState(EventState.CANCELED); 115 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 116 | } 117 | 118 | @Override 119 | @Transactional 120 | public EventOutDto updateEvent(Long eventId, EventInDto eventInDto) throws EventNotFoundException, CategoryNotFoundException { 121 | Event event = eventsRepository.findById(eventId).orElseThrow( 122 | () -> new EventNotFoundException("Event ID not found.") 123 | ); 124 | if (eventInDto.getEventDate() != null) { 125 | event.setEventDate(eventInDto.getEventDate()); 126 | } 127 | Utils.setNotNullParamToEntity(eventInDto, event, categoriesRepository); 128 | 129 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminUsersService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import ru.practicum.ewm.dto.users.UserDto; 4 | import ru.practicum.ewm.exception.UserNotFoundException; 5 | 6 | import java.util.List; 7 | 8 | public interface AdminUsersService { 9 | UserDto createUser(UserDto userDto); 10 | 11 | List findAll(Long[] ids, Integer from, Integer size); 12 | 13 | void deleteUserById(Long userId) throws UserNotFoundException; 14 | } -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/admin/service/AdminUsersServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.admin.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.stereotype.Service; 7 | import ru.practicum.ewm.dto.users.UserDto; 8 | import ru.practicum.ewm.exception.UserNotFoundException; 9 | import ru.practicum.ewm.mapper.UserMapper; 10 | import ru.practicum.ewm.model.User; 11 | import ru.practicum.ewm.repository.UsersRepository; 12 | 13 | import java.security.InvalidParameterException; 14 | import java.util.List; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class AdminUsersServiceImpl implements AdminUsersService { 19 | private final UsersRepository usersRepository; 20 | 21 | @Override 22 | public UserDto createUser(UserDto userDto) { 23 | if (userDto.getEmail() == null) { 24 | throw new InvalidParameterException("User Email is empty."); 25 | } 26 | User user = usersRepository.save(UserMapper.dtoToUser(userDto)); 27 | return UserMapper.userToDto(user); 28 | } 29 | 30 | @Override 31 | public List findAll(Long[] ids, Integer from, Integer size) { 32 | Pageable pageable = PageRequest.of(from / size, size); 33 | return UserMapper.userListToDto(usersRepository.findAllByIds(ids, pageable)); 34 | } 35 | 36 | @Override 37 | public void deleteUserById(Long userId) throws UserNotFoundException { 38 | if (!usersRepository.existsById(userId)) { 39 | throw new UserNotFoundException("User with was not found."); 40 | } 41 | usersRepository.deleteById(userId); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/PublicCategoriesController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 9 | import ru.practicum.ewm.endpoints.pub.service.CategoriesService; 10 | import ru.practicum.ewm.exception.CategoryNotFoundException; 11 | 12 | import javax.validation.constraints.Positive; 13 | import javax.validation.constraints.PositiveOrZero; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/categories") 18 | @RequiredArgsConstructor 19 | @Validated 20 | @Slf4j 21 | public class PublicCategoriesController { 22 | 23 | private final CategoriesService categoriesService; 24 | 25 | @GetMapping 26 | public List findAllCategories(@PositiveOrZero 27 | @RequestParam(name = "from", defaultValue = "0") Integer from, 28 | @Positive 29 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size) { 30 | log.info("Public findAllCategories: {},{}", from, size); 31 | return categoriesService.findAllCategories(from, size); 32 | } 33 | 34 | @GetMapping("{catId}") 35 | public CategoryFullDto findCategoryById(@Positive @PathVariable Long catId) throws CategoryNotFoundException { 36 | log.info("Public findCategoryById: {}", catId); 37 | return categoriesService.findCategoryById(catId); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/PublicCompilationsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 9 | import ru.practicum.ewm.endpoints.pub.service.CompilationsService; 10 | import ru.practicum.ewm.exception.CompilationNotFoundException; 11 | 12 | import javax.validation.constraints.Positive; 13 | import javax.validation.constraints.PositiveOrZero; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/compilations") 18 | @RequiredArgsConstructor 19 | @Validated 20 | @Slf4j 21 | public class PublicCompilationsController { 22 | 23 | private final CompilationsService compilationsService; 24 | 25 | @GetMapping 26 | public List findAllCompilations( 27 | @RequestParam(name = "pinned", required = false) Boolean pinned, 28 | @PositiveOrZero 29 | @RequestParam(name = "from", defaultValue = "0") Integer from, 30 | @Positive 31 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size) { 32 | log.info("Public findAllCompilations: {},{},{}", pinned, from, size); 33 | return compilationsService.findAllCompilations(pinned, from, size); 34 | } 35 | 36 | @GetMapping("{compId}") 37 | public CompilationOutDto findCompilationById(@Positive @PathVariable Long compId) throws CompilationNotFoundException { 38 | log.info("Public findCompilationById: {}", compId); 39 | return compilationsService.findCompilationById(compId); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/PublicEventsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.events.EventPublicOutDto; 9 | import ru.practicum.ewm.endpoints.pub.service.EventsService; 10 | import ru.practicum.ewm.exception.EventNotFoundException; 11 | import ru.practicum.ewm.model.SortType; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.validation.constraints.Positive; 15 | import javax.validation.constraints.PositiveOrZero; 16 | import java.util.List; 17 | 18 | @RestController 19 | @RequestMapping("/events") 20 | @RequiredArgsConstructor 21 | @Validated 22 | @Slf4j 23 | public class PublicEventsController { 24 | 25 | private final EventsService eventsService; 26 | 27 | @GetMapping 28 | public List findAllEvents( 29 | @RequestParam(name = "text", defaultValue = "") String text, 30 | @RequestParam(name = "categories", required = false) Long[] categories, 31 | @RequestParam(name = "paid", required = false) Boolean paid, 32 | @RequestParam(name = "rangeStart", required = false) String rangeStart, 33 | @RequestParam(name = "rangeEnd", required = false) String rangeEnd, 34 | @RequestParam(name = "onlyAvailable", required = false) Boolean onlyAvailable, 35 | @RequestParam(name = "sort", defaultValue = "EVENT_DATE", required = false) String sort, 36 | @PositiveOrZero 37 | @RequestParam(name = "from", defaultValue = "0") Integer from, 38 | @Positive 39 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size, 40 | HttpServletRequest request) { 41 | log.info("Public findAllEvents: {},{},{},{},{},{},{},{},{},{}", 42 | text, categories, paid, rangeStart, rangeEnd, onlyAvailable, sort, from, size, request); 43 | SortType sortType = SortType.from(sort) 44 | .orElseThrow(() -> new IllegalArgumentException("Unknown type: " + sort)); 45 | return eventsService.findAllEvents( 46 | text, 47 | categories, 48 | paid, 49 | rangeStart, 50 | rangeEnd, 51 | onlyAvailable, 52 | sortType, 53 | from, 54 | size, 55 | request 56 | ); 57 | } 58 | 59 | @GetMapping("{eventId}") 60 | public EventPublicOutDto findEventById(@Positive @PathVariable Long eventId, HttpServletRequest request) throws EventNotFoundException { 61 | log.info("Public findEventById: {},{}", eventId, request); 62 | return eventsService.findEventById(eventId, request); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/CategoriesService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 4 | import ru.practicum.ewm.exception.CategoryNotFoundException; 5 | 6 | import java.util.List; 7 | 8 | public interface CategoriesService { 9 | List findAllCategories(Integer from, Integer size); 10 | 11 | CategoryFullDto findCategoryById(Long catId) throws CategoryNotFoundException; 12 | } 13 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/CategoriesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.stereotype.Service; 8 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 9 | import ru.practicum.ewm.exception.CategoryNotFoundException; 10 | import ru.practicum.ewm.mapper.CategoryMapper; 11 | import ru.practicum.ewm.model.Category; 12 | import ru.practicum.ewm.repository.CategoriesRepository; 13 | 14 | import java.util.List; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class CategoriesServiceImpl implements CategoriesService { 19 | private final CategoriesRepository categoriesRepository; 20 | 21 | @Override 22 | public List findAllCategories(Integer from, Integer size) { 23 | Sort sort = Sort.sort(Category.class).by(Category::getName).ascending(); 24 | Pageable pageable = PageRequest.of(from / size, size, sort); 25 | 26 | return CategoryMapper.categoryToListDtoOut(categoriesRepository.findAll(pageable).toList()); 27 | } 28 | 29 | @Override 30 | public CategoryFullDto findCategoryById(Long catId) throws CategoryNotFoundException { 31 | return CategoryMapper.categoryToDtoOut(categoriesRepository.findById(catId).orElseThrow( 32 | () -> new CategoryNotFoundException("Category ID was not found.") 33 | )); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/CompilationsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 4 | import ru.practicum.ewm.exception.CompilationNotFoundException; 5 | 6 | import java.util.List; 7 | 8 | public interface CompilationsService { 9 | List findAllCompilations(Boolean pinned, Integer from, Integer size); 10 | 11 | CompilationOutDto findCompilationById(Long compId) throws CompilationNotFoundException; 12 | } 13 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/CompilationsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.stereotype.Service; 7 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 8 | import ru.practicum.ewm.exception.CompilationNotFoundException; 9 | import ru.practicum.ewm.mapper.CompilationMapper; 10 | import ru.practicum.ewm.repository.CompilationsRepository; 11 | 12 | import java.util.List; 13 | 14 | @Service 15 | @RequiredArgsConstructor 16 | public class CompilationsServiceImpl implements CompilationsService { 17 | private final CompilationsRepository compilationsRepository; 18 | 19 | @Override 20 | public List findAllCompilations(Boolean pinned, Integer from, Integer size) { 21 | Pageable pageable = PageRequest.of(from / size, size); 22 | if (pinned != null) { 23 | return CompilationMapper.compilationToListOutDto(compilationsRepository.findAlLByPinned(pinned, pageable)); 24 | } 25 | return CompilationMapper.compilationToListOutDto(compilationsRepository.findAll(pageable).toList()); 26 | } 27 | 28 | @Override 29 | public CompilationOutDto findCompilationById(Long compId) throws CompilationNotFoundException { 30 | return CompilationMapper.compilationToOutDto(compilationsRepository.findById(compId).orElseThrow( 31 | () -> new CompilationNotFoundException("Compilation ID was not found.") 32 | )); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/EventsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import ru.practicum.ewm.dto.events.EventPublicOutDto; 4 | import ru.practicum.ewm.exception.EventNotFoundException; 5 | import ru.practicum.ewm.model.SortType; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import java.util.List; 9 | 10 | public interface EventsService { 11 | EventPublicOutDto findEventById(Long eventId, HttpServletRequest request) throws EventNotFoundException; 12 | 13 | List findAllEvents(String text, 14 | Long[] categories, 15 | Boolean paid, 16 | String rangeStart, 17 | String rangeEnd, 18 | Boolean onlyAvailable, 19 | SortType sortType, 20 | Integer from, 21 | Integer size, 22 | HttpServletRequest request); 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/pub/service/EventsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.pub.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.data.domain.PageRequest; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.stereotype.Service; 9 | import ru.practicum.ewm.Constants; 10 | import ru.practicum.ewm.MainAPI; 11 | import ru.practicum.ewm.dto.events.EventPublicOutDto; 12 | import ru.practicum.ewm.dto.stats.StatInDto; 13 | import ru.practicum.ewm.exception.EventNotFoundException; 14 | import ru.practicum.ewm.mapper.EventMapper; 15 | import ru.practicum.ewm.model.Event; 16 | import ru.practicum.ewm.model.EventState; 17 | import ru.practicum.ewm.model.SortType; 18 | import ru.practicum.ewm.repository.EventsRepository; 19 | import ru.practicum.ewm.utils.AdminStatsClient; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import java.time.LocalDateTime; 23 | import java.util.List; 24 | 25 | @Service 26 | @RequiredArgsConstructor 27 | @Slf4j 28 | public class EventsServiceImpl implements EventsService { 29 | private final EventsRepository eventsRepository; 30 | private final AdminStatsClient adminStatsClient; 31 | 32 | @Override 33 | public EventPublicOutDto findEventById(Long eventId, HttpServletRequest request) throws EventNotFoundException { 34 | if (!eventsRepository.existsByIdAndState(eventId, EventState.PUBLISHED)) { 35 | throw new EventNotFoundException("Event not found."); 36 | } 37 | Event event = eventsRepository.findByIdAndState(eventId, EventState.PUBLISHED).orElseThrow( 38 | () -> new EventNotFoundException("Event not found.") 39 | ); 40 | eventsRepository.incrementViews(eventId); 41 | 42 | Thread sendHit = new Thread( 43 | () -> { 44 | try { 45 | adminStatsClient.saveHit(new StatInDto( 46 | MainAPI.APP_NAME, 47 | request.getRequestURI(), 48 | request.getRemoteAddr(), 49 | LocalDateTime.now() 50 | )); 51 | log.info(">>Hit send - OK."); 52 | } catch (Exception err) { 53 | log.info(">>Hit send. Error: " + err.getMessage()); 54 | } 55 | }); 56 | sendHit.start(); 57 | return EventMapper.eventToPublicOutDto(event); 58 | } 59 | 60 | @Override 61 | public List findAllEvents(String text, 62 | Long[] categories, 63 | Boolean paid, 64 | String rangeStart, 65 | String rangeEnd, 66 | Boolean onlyAvailable, 67 | SortType sortType, 68 | Integer from, 69 | Integer size, HttpServletRequest request) { 70 | 71 | Sort sort; 72 | switch (sortType) { 73 | case EVENT_DATE: 74 | sort = Sort.sort(Event.class).by(Event::getEventDate).ascending(); 75 | break; 76 | case VIEWS: 77 | sort = Sort.sort(Event.class).by(Event::getViews).descending(); 78 | break; 79 | case RATE: 80 | sort = Sort.sort(Event.class).by(Event::getRate).descending(); 81 | break; 82 | default: 83 | throw new IllegalArgumentException("Указан не существующий тип сортировки."); 84 | } 85 | 86 | Pageable pageable = PageRequest.of(from / size, size, sort); 87 | 88 | LocalDateTime startDate; 89 | if (rangeStart != null) { 90 | startDate = LocalDateTime.parse(rangeStart, Constants.DATE_TIME_SPACE); 91 | } else { 92 | startDate = LocalDateTime.now(); 93 | } 94 | LocalDateTime endDate; 95 | if (rangeStart != null) { 96 | endDate = LocalDateTime.parse(rangeEnd, Constants.DATE_TIME_SPACE); 97 | } else { 98 | endDate = LocalDateTime.now(); 99 | } 100 | 101 | onlyAvailable = true; 102 | List events = eventsRepository.findAllByParam( 103 | text, 104 | categories, 105 | paid, 106 | startDate, 107 | endDate, 108 | onlyAvailable, 109 | pageable); 110 | 111 | Thread sendHit = new Thread( 112 | () -> { 113 | try { 114 | adminStatsClient.saveHit(new StatInDto( 115 | MainAPI.APP_NAME, 116 | request.getRequestURI(), 117 | request.getRemoteAddr(), 118 | LocalDateTime.now() 119 | )); 120 | log.info(">>Hit search send - OK."); 121 | } catch (Exception err) { 122 | log.info(">>Hit search send. Error: " + err.getMessage()); 123 | } 124 | }); 125 | sendHit.start(); 126 | 127 | return EventMapper.eventToPublicListOutDto(events); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/UsersEventsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.events.EventInDto; 9 | import ru.practicum.ewm.dto.events.EventOutDto; 10 | import ru.practicum.ewm.endpoints.user.service.UsersEventsService; 11 | import ru.practicum.ewm.exception.*; 12 | import ru.practicum.ewm.model.LikeType; 13 | 14 | import javax.validation.Valid; 15 | import javax.validation.constraints.Positive; 16 | import javax.validation.constraints.PositiveOrZero; 17 | import java.nio.file.AccessDeniedException; 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping("/users/{userId}/events") 22 | @RequiredArgsConstructor 23 | @Validated 24 | @Slf4j 25 | public class UsersEventsController { 26 | private final UsersEventsService usersEventsService; 27 | 28 | @PostMapping 29 | public EventOutDto addEvent(@Positive @PathVariable Long userId, @Valid @RequestBody EventInDto eventInDto) 30 | throws CategoryNotFoundException, UserNotFoundException { 31 | log.info("User addEvent: {},{}", userId, eventInDto); 32 | return usersEventsService.addEvent(userId, eventInDto); 33 | } 34 | 35 | @PatchMapping 36 | public EventOutDto updateEvent(@Positive @PathVariable Long userId, @Valid @RequestBody EventInDto eventInDto) 37 | throws 38 | CategoryNotFoundException, 39 | UserNotFoundException, 40 | EventNotFoundException, 41 | EventClosedException { 42 | log.info("User updateEvent: {},{}", userId, eventInDto); 43 | return usersEventsService.updateEvent(userId, eventInDto); 44 | } 45 | 46 | @GetMapping 47 | public List findAllEvents(@Positive @PathVariable Long userId, 48 | @PositiveOrZero 49 | @RequestParam(name = "from", defaultValue = "0") Integer from, 50 | @Positive 51 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size) 52 | throws UserNotFoundException { 53 | log.info("User findAllEvents: {},{},{}", userId, from, size); 54 | return usersEventsService.findAllEvents(userId, from, size); 55 | } 56 | 57 | @GetMapping("{eventId}") 58 | public EventOutDto getEvent(@Positive @PathVariable Long userId, 59 | @Positive @PathVariable Long eventId) 60 | throws UserNotFoundException, EventNotFoundException { 61 | log.info("User getEvent: {},{}", userId, eventId); 62 | return usersEventsService.getEvent(userId, eventId); 63 | } 64 | 65 | @PatchMapping("{eventId}") 66 | public EventOutDto cancelEvent(@Positive @PathVariable Long userId, 67 | @Positive @PathVariable Long eventId) 68 | throws UserNotFoundException, EventNotFoundException, EventClosedException { 69 | log.info("User cancelEvent: {},{}", userId, eventId); 70 | return usersEventsService.cancelEvent(userId, eventId); 71 | } 72 | 73 | @PutMapping("/{eventId}/like") 74 | public void addLike( 75 | @Positive @PathVariable Long userId, 76 | @Positive @PathVariable Long eventId, 77 | @RequestParam(name = "type") String type 78 | ) throws UserNotFoundException, 79 | EventNotFoundException, 80 | DoubleLikeException, 81 | LikeNotFoundException, 82 | AccessDeniedException { 83 | LikeType likeType = LikeType.from(type) 84 | .orElseThrow(() -> new IllegalArgumentException("Unknown type: " + type)); 85 | usersEventsService.addLike(userId, eventId, likeType); 86 | } 87 | 88 | @DeleteMapping("/{eventId}/like") 89 | public void removeLike( 90 | @Positive @PathVariable Long userId, 91 | @Positive @PathVariable Long eventId, 92 | @RequestParam(name = "type") String type 93 | ) throws UserNotFoundException, EventNotFoundException, LikeNotFoundException, AccessDeniedException { 94 | LikeType likeType = LikeType.from(type) 95 | .orElseThrow(() -> new IllegalArgumentException("Unknown type: " + type)); 96 | usersEventsService.removeLike(userId, eventId, likeType); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/UsersEventsRequestsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.validation.annotation.Validated; 5 | import org.springframework.web.bind.annotation.*; 6 | import ru.practicum.ewm.dto.requests.RequestOutDto; 7 | import ru.practicum.ewm.endpoints.user.service.RequestNotFoundException; 8 | import ru.practicum.ewm.endpoints.user.service.UsersEventsRequestsService; 9 | import ru.practicum.ewm.exception.EventNotFoundException; 10 | import ru.practicum.ewm.exception.UserNotFoundException; 11 | 12 | import javax.validation.constraints.Positive; 13 | import java.nio.file.AccessDeniedException; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/users/{userId}/events/{eventId}/requests") 18 | @RequiredArgsConstructor 19 | @Validated 20 | public class UsersEventsRequestsController { 21 | private final UsersEventsRequestsService usersEventsRequestsService; 22 | 23 | @GetMapping 24 | public List findAllEventRequests(@Positive @PathVariable Long userId, 25 | @Positive @PathVariable Long eventId) 26 | throws UserNotFoundException, EventNotFoundException { 27 | return usersEventsRequestsService.findAllEventRequests(userId, eventId); 28 | } 29 | 30 | @PatchMapping("{requestId}/confirm") 31 | public RequestOutDto confirmRequest(@Positive @PathVariable Long userId, 32 | @Positive @PathVariable Long eventId, 33 | @Positive @PathVariable Long requestId) 34 | throws RequestNotFoundException, AccessDeniedException, UserNotFoundException { 35 | return usersEventsRequestsService.confirmRequest(userId, eventId, requestId); 36 | } 37 | 38 | @PatchMapping("{requestId}/reject") 39 | public RequestOutDto rejectRequest(@Positive @PathVariable Long userId, 40 | @Positive @PathVariable Long eventId, 41 | @Positive @PathVariable Long requestId) 42 | throws RequestNotFoundException, AccessDeniedException, UserNotFoundException { 43 | return usersEventsRequestsService.rejectRequest(userId, eventId, requestId); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/UsersRequestsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.validation.annotation.Validated; 5 | import org.springframework.web.bind.annotation.*; 6 | import ru.practicum.ewm.dto.requests.RequestOutDto; 7 | import ru.practicum.ewm.endpoints.user.service.RequestNotFoundException; 8 | import ru.practicum.ewm.endpoints.user.service.RequestsService; 9 | import ru.practicum.ewm.exception.EventNotFoundException; 10 | import ru.practicum.ewm.exception.UserNotFoundException; 11 | import ru.practicum.ewm.exception.UserRequestHimselfException; 12 | 13 | import javax.validation.constraints.Positive; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/users/{userId}/requests") 18 | @RequiredArgsConstructor 19 | @Validated 20 | public class UsersRequestsController { 21 | private final RequestsService requestsService; 22 | 23 | @PostMapping 24 | public RequestOutDto addRequest(@Positive @PathVariable Long userId, 25 | @Positive @RequestParam(name = "eventId") Long eventId) 26 | throws UserNotFoundException, EventNotFoundException, UserRequestHimselfException { 27 | return requestsService.addRequest(userId, eventId); 28 | } 29 | 30 | @GetMapping 31 | public List findAllRequests(@Positive @PathVariable Long userId) 32 | throws UserNotFoundException { 33 | return requestsService.findAllRequests(userId); 34 | } 35 | 36 | @PatchMapping("{requestId}/cancel") 37 | public RequestOutDto cancelRequest(@Positive @PathVariable Long userId, 38 | @Positive @PathVariable Long requestId) 39 | throws UserNotFoundException, RequestNotFoundException { 40 | return requestsService.cancelRequest(userId, requestId); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/RequestNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | public class RequestNotFoundException extends Exception { 4 | 5 | public RequestNotFoundException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/RequestsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import ru.practicum.ewm.dto.requests.RequestOutDto; 4 | import ru.practicum.ewm.exception.EventNotFoundException; 5 | import ru.practicum.ewm.exception.UserNotFoundException; 6 | import ru.practicum.ewm.exception.UserRequestHimselfException; 7 | 8 | import java.util.List; 9 | 10 | public interface RequestsService { 11 | RequestOutDto addRequest(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException, UserRequestHimselfException; 12 | 13 | List findAllRequests(Long userId) throws UserNotFoundException; 14 | 15 | RequestOutDto cancelRequest(Long userId, Long requestId) throws UserNotFoundException, RequestNotFoundException; 16 | } 17 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/RequestsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import ru.practicum.ewm.dto.requests.RequestOutDto; 6 | import ru.practicum.ewm.exception.EventNotFoundException; 7 | import ru.practicum.ewm.exception.UserNotFoundException; 8 | import ru.practicum.ewm.exception.UserRequestHimselfException; 9 | import ru.practicum.ewm.mapper.RequestMapper; 10 | import ru.practicum.ewm.model.*; 11 | import ru.practicum.ewm.repository.EventsRepository; 12 | import ru.practicum.ewm.repository.RequestsRepository; 13 | import ru.practicum.ewm.repository.UsersRepository; 14 | 15 | import java.time.LocalDateTime; 16 | import java.util.List; 17 | 18 | @Service 19 | @RequiredArgsConstructor 20 | public class RequestsServiceImpl implements RequestsService { 21 | private final RequestsRepository requestsRepository; 22 | private final UsersRepository usersRepository; 23 | private final EventsRepository eventsRepository; 24 | 25 | @Override 26 | public RequestOutDto addRequest(Long userId, Long eventId) 27 | throws UserNotFoundException, EventNotFoundException, UserRequestHimselfException { 28 | 29 | User user = usersRepository.findById(userId).orElseThrow( 30 | () -> new UserNotFoundException("User ID not found.") 31 | ); 32 | Event event = eventsRepository.findById(eventId).orElseThrow( 33 | () -> new EventNotFoundException("Event ID not found.") 34 | ); 35 | if (event.getInitiator().getId().equals(userId)) { 36 | throw new UserRequestHimselfException("User can't request himself."); 37 | } 38 | if (event.getState() != EventState.PUBLISHED) { 39 | throw new IllegalStateException("Event don't PUBLISHED."); 40 | } 41 | if (event.getParticipantLimit() != 0 && (event.getParticipantLimit() - event.getConfirmedRequests()) <= 0) { 42 | throw new IllegalStateException("Event don't have any free slot."); 43 | } 44 | 45 | RequestState newRequestState = RequestState.PENDING; 46 | if (!event.getRequestModeration()) { 47 | newRequestState = RequestState.CONFIRMED; 48 | event.setConfirmedRequests(event.getConfirmedRequests() + 1); 49 | } 50 | 51 | Request request = new Request( 52 | null, 53 | user, 54 | LocalDateTime.now(), 55 | newRequestState, 56 | event 57 | ); 58 | return RequestMapper.requestToOutDto(requestsRepository.saveAndFlush(request)); 59 | } 60 | 61 | @Override 62 | public List findAllRequests(Long userId) throws UserNotFoundException { 63 | if (!usersRepository.existsById(userId)) { 64 | throw new UserNotFoundException("User ID not found."); 65 | } 66 | return RequestMapper.requestsToListOutDto(requestsRepository.findAllByRequesterId(userId)); 67 | } 68 | 69 | @Override 70 | public RequestOutDto cancelRequest(Long userId, Long requestId) throws UserNotFoundException, RequestNotFoundException { 71 | if (!usersRepository.existsById(userId)) { 72 | throw new UserNotFoundException("User ID not found."); 73 | } 74 | Request request = requestsRepository.findById(requestId).orElseThrow( 75 | () -> new RequestNotFoundException("Request ID not found.") 76 | ); 77 | request.setStatus(RequestState.CANCELED); 78 | return RequestMapper.requestToOutDto(requestsRepository.saveAndFlush(request)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/UsersEventsRequestsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import ru.practicum.ewm.dto.requests.RequestOutDto; 4 | import ru.practicum.ewm.exception.EventNotFoundException; 5 | import ru.practicum.ewm.exception.UserNotFoundException; 6 | 7 | import java.nio.file.AccessDeniedException; 8 | import java.util.List; 9 | 10 | public interface UsersEventsRequestsService { 11 | RequestOutDto rejectRequest(Long userId, Long eventId, Long requestId) throws RequestNotFoundException, AccessDeniedException, UserNotFoundException; 12 | 13 | RequestOutDto confirmRequest(Long userId, Long eventId, Long requestId) throws UserNotFoundException, RequestNotFoundException, AccessDeniedException; 14 | 15 | List findAllEventRequests(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException; 16 | } 17 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/UsersEventsRequestsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | import ru.practicum.ewm.dto.requests.RequestOutDto; 7 | import ru.practicum.ewm.exception.EventNotFoundException; 8 | import ru.practicum.ewm.exception.UserNotFoundException; 9 | import ru.practicum.ewm.mapper.RequestMapper; 10 | import ru.practicum.ewm.model.Event; 11 | import ru.practicum.ewm.model.Request; 12 | import ru.practicum.ewm.model.RequestState; 13 | import ru.practicum.ewm.repository.EventsRepository; 14 | import ru.practicum.ewm.repository.RequestsRepository; 15 | import ru.practicum.ewm.repository.UsersRepository; 16 | 17 | import java.nio.file.AccessDeniedException; 18 | import java.util.List; 19 | 20 | @Service 21 | @RequiredArgsConstructor 22 | public class UsersEventsRequestsServiceImpl implements UsersEventsRequestsService { 23 | private final RequestsRepository requestsRepository; 24 | private final UsersRepository usersRepository; 25 | private final EventsRepository eventsRepository; 26 | 27 | @Override 28 | @Transactional 29 | public RequestOutDto confirmRequest(Long userId, Long eventId, Long requestId) throws UserNotFoundException, RequestNotFoundException, AccessDeniedException { 30 | if (!usersRepository.existsById(userId)) { 31 | throw new UserNotFoundException("User ID not found."); 32 | } 33 | Request request = requestsRepository.findById(requestId).orElseThrow( 34 | () -> new RequestNotFoundException("Request ID not found.") 35 | ); 36 | if (request.getStatus() != RequestState.PENDING) { 37 | throw new IllegalStateException("Request status can be PENDING."); 38 | } 39 | if (!request.getEvent().getId().equals(eventId)) { 40 | throw new IllegalArgumentException("Wrong Event ID for this Request."); 41 | } 42 | if (!request.getEvent().getInitiator().getId().equals(userId)) { 43 | throw new AccessDeniedException("Only owner of Event can Reject Request."); 44 | } 45 | Event event = request.getEvent(); 46 | if (event.getParticipantLimit() != 0 && (event.getParticipantLimit() - event.getConfirmedRequests()) <= 0) { 47 | requestsRepository.rejectAllPendingRequest(eventId); 48 | throw new IllegalStateException("Event don't have any free slot."); 49 | } 50 | 51 | if (request.getStatus() == RequestState.PENDING) { 52 | request.setStatus(RequestState.CONFIRMED); 53 | event.setConfirmedRequests(event.getConfirmedRequests() + 1); 54 | requestsRepository.saveAndFlush(request); 55 | } 56 | 57 | return RequestMapper.requestToOutDto(request); 58 | } 59 | 60 | @Override 61 | public RequestOutDto rejectRequest(Long userId, Long eventId, Long requestId) throws RequestNotFoundException, AccessDeniedException, UserNotFoundException { 62 | if (!usersRepository.existsById(userId)) { 63 | throw new UserNotFoundException("User ID not found."); 64 | } 65 | Request request = requestsRepository.findById(requestId).orElseThrow( 66 | () -> new RequestNotFoundException("Request ID not found.") 67 | ); 68 | if (!request.getEvent().getId().equals(eventId)) { 69 | throw new IllegalArgumentException("Wrong Event ID for this Request."); 70 | } 71 | if (!request.getEvent().getInitiator().getId().equals(userId)) { 72 | throw new AccessDeniedException("Only owner of Event can Reject Request."); 73 | } 74 | request.setStatus(RequestState.REJECTED); 75 | return RequestMapper.requestToOutDto(requestsRepository.saveAndFlush(request)); 76 | } 77 | 78 | @Override 79 | public List findAllEventRequests(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException { 80 | if (!usersRepository.existsById(userId)) { 81 | throw new UserNotFoundException("User ID not found."); 82 | } 83 | if (!eventsRepository.existsById(eventId)) { 84 | throw new EventNotFoundException("Event ID not found."); 85 | } 86 | return RequestMapper.requestsToListOutDto(requestsRepository.findAllByInitiatorIdAndEventId(userId, eventId)); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/UsersEventsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import ru.practicum.ewm.dto.events.EventInDto; 4 | import ru.practicum.ewm.dto.events.EventOutDto; 5 | import ru.practicum.ewm.exception.*; 6 | import ru.practicum.ewm.model.LikeType; 7 | 8 | import java.nio.file.AccessDeniedException; 9 | import java.util.List; 10 | 11 | public interface UsersEventsService { 12 | EventOutDto addEvent(Long userId, EventInDto eventInDto) throws CategoryNotFoundException, UserNotFoundException; 13 | 14 | EventOutDto updateEvent(Long userId, EventInDto eventInDto) throws CategoryNotFoundException, UserNotFoundException, EventNotFoundException, EventClosedException; 15 | 16 | List findAllEvents(Long userId, Integer from, Integer size) throws UserNotFoundException; 17 | 18 | EventOutDto getEvent(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException; 19 | 20 | EventOutDto cancelEvent(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException, EventClosedException; 21 | 22 | void addLike(Long userId, Long eventId, LikeType likeType) throws UserNotFoundException, EventNotFoundException, DoubleLikeException, LikeNotFoundException, AccessDeniedException; 23 | 24 | void removeLike(Long userId, Long eventId, LikeType likeType) throws UserNotFoundException, EventNotFoundException, LikeNotFoundException, AccessDeniedException; 25 | } 26 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/endpoints/user/service/UsersEventsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.endpoints.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | import ru.practicum.ewm.Constants; 10 | import ru.practicum.ewm.dto.events.EventInDto; 11 | import ru.practicum.ewm.dto.events.EventOutDto; 12 | import ru.practicum.ewm.exception.*; 13 | import ru.practicum.ewm.mapper.EventMapper; 14 | import ru.practicum.ewm.model.*; 15 | import ru.practicum.ewm.repository.*; 16 | import ru.practicum.ewm.utils.Utils; 17 | 18 | import java.nio.file.AccessDeniedException; 19 | import java.security.InvalidParameterException; 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | @Service 24 | @RequiredArgsConstructor 25 | public class UsersEventsServiceImpl implements UsersEventsService { 26 | private final EventsRepository eventsRepository; 27 | private final CategoriesRepository categoriesRepository; 28 | private final UsersRepository usersRepository; 29 | private final LikeRepository likeRepository; 30 | private final RequestsRepository requestsRepository; 31 | 32 | @Override 33 | @Transactional 34 | public EventOutDto addEvent(Long userId, EventInDto eventInDto) throws CategoryNotFoundException, UserNotFoundException { 35 | if (!categoriesRepository.existsById(eventInDto.getCategory())) { 36 | throw new CategoryNotFoundException("Category ID not found."); 37 | } 38 | if (!usersRepository.existsById(userId)) { 39 | throw new UserNotFoundException("User ID not found."); 40 | } 41 | if (eventInDto.getLocation() == null) { 42 | throw new InvalidParameterException("Location is null."); 43 | } 44 | if (eventInDto.getPaid() == null) { 45 | throw new InvalidParameterException("Paid is null."); 46 | } 47 | Utils.checkTimeBeforeOrThrow(eventInDto.getEventDate(), Constants.USER_TIME_HOUR_BEFORE_START); 48 | 49 | Event event = EventMapper.dtoInToEvent(eventInDto, categoriesRepository.getReferenceById(eventInDto.getCategory())); 50 | event.setInitiator(usersRepository.getReferenceById(userId)); 51 | event.setState(EventState.PENDING); 52 | event.setConfirmedRequests(0); 53 | event.setViews(0L); 54 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 55 | } 56 | 57 | @Override 58 | @Transactional 59 | public EventOutDto updateEvent(Long userId, EventInDto eventInDto) 60 | throws CategoryNotFoundException, UserNotFoundException, EventNotFoundException, EventClosedException { 61 | if (!usersRepository.existsById(userId)) { 62 | throw new UserNotFoundException("User ID not found."); 63 | } 64 | Event event = eventsRepository.findById(eventInDto.getEventId()).orElseThrow( 65 | () -> new EventNotFoundException("Event ID not found.") 66 | ); 67 | 68 | if (event.getState() == EventState.PUBLISHED) { 69 | throw new EventClosedException("Event is published."); 70 | } else if (event.getState() == EventState.CANCELED) { 71 | event.setState(EventState.PENDING); 72 | } 73 | 74 | if (eventInDto.getEventDate() != null) { 75 | event.setEventDate(eventInDto.getEventDate()); 76 | } 77 | Utils.checkTimeBeforeOrThrow(event.getEventDate(), Constants.USER_TIME_HOUR_BEFORE_START); 78 | Utils.setNotNullParamToEntity(eventInDto, event, categoriesRepository); 79 | 80 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 81 | } 82 | 83 | @Override 84 | public List findAllEvents(Long userId, Integer from, Integer size) throws UserNotFoundException { 85 | if (!usersRepository.existsById(userId)) { 86 | throw new UserNotFoundException("User ID not found."); 87 | } 88 | Sort sort = Sort.sort(Event.class).by(Event::getEventDate).descending(); 89 | Pageable pageable = PageRequest.of(from / size, size, sort); 90 | return EventMapper.eventToListOutDto(eventsRepository.findAllByInitiatorId(userId, pageable)); 91 | } 92 | 93 | @Override 94 | public EventOutDto getEvent(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException { 95 | if (!usersRepository.existsById(userId)) { 96 | throw new UserNotFoundException("User ID not found."); 97 | } 98 | Event event = eventsRepository.findById(eventId).orElseThrow( 99 | () -> new EventNotFoundException("Event ID not found.") 100 | ); 101 | 102 | return EventMapper.eventToOutDto(event); 103 | } 104 | 105 | @Override 106 | public EventOutDto cancelEvent(Long userId, Long eventId) throws UserNotFoundException, EventNotFoundException, EventClosedException { 107 | if (!usersRepository.existsById(userId)) { 108 | throw new UserNotFoundException("User ID not found."); 109 | } 110 | Event event = eventsRepository.findById(eventId).orElseThrow( 111 | () -> new EventNotFoundException("Event ID not found.") 112 | ); 113 | if (event.getState() != EventState.PENDING) { 114 | throw new EventClosedException("Event is not pending."); 115 | } 116 | event.setState(EventState.CANCELED); 117 | 118 | return EventMapper.eventToOutDto(eventsRepository.saveAndFlush(event)); 119 | } 120 | 121 | @Override 122 | @Transactional 123 | public void addLike(Long userId, Long eventId, LikeType likeType) 124 | throws UserNotFoundException, EventNotFoundException, DoubleLikeException, LikeNotFoundException, AccessDeniedException { 125 | Event event = getEvent(eventId); 126 | checkUser(userId, event); 127 | if (!requestsRepository.existsByRequesterIdAndEventIdAndStatus(userId, eventId, RequestState.CONFIRMED)) { 128 | throw new AccessDeniedException("Запрещено оценивать событие в которых не участвуешь."); 129 | } 130 | 131 | Optional like = likeRepository.findByEventIdAndUserId(userId, eventId); 132 | if (like.isPresent()) { 133 | if (like.get().getType() != likeType) { 134 | LikeType deleteType = LikeType.LIKE; 135 | if (likeType == LikeType.LIKE) { 136 | deleteType = LikeType.DISLIKE; 137 | } 138 | removeLike(userId, eventId, deleteType); 139 | } else { 140 | throw new DoubleLikeException("Можно поставить только один раз."); 141 | } 142 | } 143 | likeRepository.saveAndFlush(new Like(null, userId, eventId, likeType)); 144 | if (likeType == LikeType.LIKE) { 145 | eventsRepository.incrementRate(eventId); 146 | } else { 147 | eventsRepository.decrementRate(eventId); 148 | } 149 | 150 | User initiator = event.getInitiator(); 151 | initiator.setRate(getRate(initiator.getId())); 152 | usersRepository.save(initiator); 153 | } 154 | 155 | @Override 156 | @Transactional 157 | public void removeLike(Long userId, Long eventId, LikeType likeType) 158 | throws UserNotFoundException, EventNotFoundException, LikeNotFoundException, AccessDeniedException { 159 | Event event = getEvent(eventId); 160 | checkUser(userId, event); 161 | 162 | Like like = likeRepository.findByUserIdAndEventIdAndType(userId, eventId, likeType) 163 | .orElseThrow( 164 | () -> new LikeNotFoundException(likeType + " not found.") 165 | ); 166 | likeRepository.delete(like); 167 | 168 | if (likeType == LikeType.LIKE) { 169 | eventsRepository.decrementRate(eventId); 170 | } else { 171 | eventsRepository.incrementRate(eventId); 172 | } 173 | 174 | User initiator = event.getInitiator(); 175 | initiator.setRate(getRate(initiator.getId())); 176 | usersRepository.save(initiator); 177 | } 178 | 179 | private Float getRate(Long userId) { 180 | int count = eventsRepository.countByInitiatorId(userId); 181 | long rate = eventsRepository.sumRateByInitiatorId(userId); 182 | 183 | return count == 0 ? 0.0F : (1.0F * rate / count); 184 | } 185 | 186 | private void checkUser(Long userId, Event event) throws UserNotFoundException, AccessDeniedException { 187 | if (!usersRepository.existsById(userId)) { 188 | throw new UserNotFoundException("User ID not found."); 189 | } 190 | if (userId.equals(event.getInitiator().getId())) { 191 | throw new AccessDeniedException("Запрещено оценивать собственное событие."); 192 | } 193 | } 194 | 195 | private Event getEvent(Long eventId) throws EventNotFoundException, AccessDeniedException { 196 | Event event = eventsRepository.findById(eventId).orElseThrow( 197 | () -> new EventNotFoundException("Event ID not found.") 198 | ); 199 | if (event.getState() != EventState.PUBLISHED) { 200 | throw new AccessDeniedException("Можно оценивать только опубликованные события."); 201 | } 202 | return event; 203 | } 204 | 205 | } -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/CategoryNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class CategoryNotFoundException extends Exception { 4 | public CategoryNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/CompilationNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class CompilationNotFoundException extends Exception { 4 | public CompilationNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/DoubleLikeException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class DoubleLikeException extends Exception { 4 | public DoubleLikeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/EventClosedException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class EventClosedException extends Exception { 4 | public EventClosedException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/EventNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class EventNotFoundException extends Exception { 4 | public EventNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/LikeNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class LikeNotFoundException extends Exception { 4 | public LikeNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class UserNotFoundException extends Exception { 4 | public UserNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/exception/UserRequestHimselfException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.exception; 2 | 3 | public class UserRequestHimselfException extends Exception { 4 | public UserRequestHimselfException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 4 | import ru.practicum.ewm.dto.categories.CategoryInDto; 5 | import ru.practicum.ewm.model.Category; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class CategoryMapper { 11 | public static Category dtoOutToCategory(CategoryFullDto categoryFullDto) { 12 | return new Category( 13 | categoryFullDto.getId(), 14 | categoryFullDto.getName() 15 | ); 16 | } 17 | 18 | public static CategoryFullDto categoryToDtoOut(Category category) { 19 | return new CategoryFullDto( 20 | category.getId(), 21 | category.getName() 22 | ); 23 | } 24 | 25 | public static Category dtoInToCategory(CategoryInDto categoryInDto) { 26 | Category category = new Category(); 27 | category.setName(categoryInDto.getName()); 28 | return category; 29 | } 30 | 31 | public static List categoryToListDtoOut(List listCategories) { 32 | List categoryFullDtoList = new ArrayList<>(); 33 | for (Category category : listCategories) { 34 | categoryFullDtoList.add(categoryToDtoOut(category)); 35 | } 36 | return categoryFullDtoList; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.compilations.CompilationInDto; 4 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 5 | import ru.practicum.ewm.model.Compilation; 6 | import ru.practicum.ewm.model.Event; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class CompilationMapper { 12 | public static Compilation dtoToCompilation(CompilationInDto compilationInDto, List events) { 13 | return new Compilation( 14 | null, 15 | compilationInDto.getTitle(), 16 | compilationInDto.getPinned(), 17 | events 18 | ); 19 | } 20 | 21 | public static CompilationOutDto compilationToOutDto(Compilation compilation) { 22 | return new CompilationOutDto( 23 | compilation.getId(), 24 | compilation.getTitle(), 25 | EventMapper.eventToListOutDto(compilation.getEvents()), 26 | compilation.getPinned() 27 | ); 28 | } 29 | 30 | public static List compilationToListOutDto(List compilationList) { 31 | List compilationOutDtoList = new ArrayList<>(); 32 | for (Compilation compilation : compilationList) { 33 | compilationOutDtoList.add(compilationToOutDto(compilation)); 34 | } 35 | return compilationOutDtoList; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/EventMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.events.EventInDto; 4 | import ru.practicum.ewm.dto.events.EventOutDto; 5 | import ru.practicum.ewm.dto.events.EventPublicOutDto; 6 | import ru.practicum.ewm.model.Category; 7 | import ru.practicum.ewm.model.Event; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class EventMapper { 13 | public static Event dtoInToEvent(EventInDto eventInDto, Category category) { 14 | return Event.builder() 15 | .annotation(eventInDto.getAnnotation()) 16 | .category(category) 17 | .description(eventInDto.getDescription()) 18 | .location(LocationMapper.dtoToLocation(eventInDto.getLocation())) 19 | .title(eventInDto.getTitle()) 20 | .eventDate(eventInDto.getEventDate()) 21 | .paid(eventInDto.getPaid()) 22 | .participantLimit(eventInDto.getParticipantLimit()) 23 | .requestModeration(eventInDto.getRequestModeration()) 24 | .build(); 25 | } 26 | 27 | public static EventOutDto eventToOutDto(Event event) { 28 | return EventOutDto.builder() 29 | .annotation(event.getAnnotation()) 30 | .category(CategoryMapper.categoryToDtoOut(event.getCategory())) 31 | .initiator(UserMapper.userToDto(event.getInitiator())) 32 | .location(LocationMapper.locationToDto(event.getLocation())) 33 | .title(event.getTitle()) 34 | .confirmedRequests(event.getConfirmedRequests()) 35 | .createdOn(event.getCreatedOn()) 36 | .description(event.getDescription()) 37 | .eventDate(event.getEventDate()) 38 | .id(event.getId()) 39 | .paid(event.getPaid()) 40 | .participantLimit(event.getParticipantLimit()) 41 | .publishedOn(event.getPublishedOn()) 42 | .requestModeration(event.getRequestModeration()) 43 | .state(event.getState()) 44 | .views(event.getViews()) 45 | .rate(event.getRate()) 46 | .build(); 47 | } 48 | 49 | public static List eventToListOutDto(List listEvents) { 50 | List eventOutDtoList = new ArrayList<>(); 51 | for (Event event : listEvents) { 52 | eventOutDtoList.add(eventToOutDto(event)); 53 | } 54 | return eventOutDtoList; 55 | } 56 | 57 | 58 | public static EventPublicOutDto eventToPublicOutDto(Event event) { 59 | return EventPublicOutDto.builder() 60 | .annotation(event.getAnnotation()) 61 | .category(CategoryMapper.categoryToDtoOut(event.getCategory())) 62 | .initiator(UserMapper.userToPublicDto(event.getInitiator())) 63 | .location(LocationMapper.locationToDto(event.getLocation())) 64 | .title(event.getTitle()) 65 | .confirmedRequests(event.getConfirmedRequests()) 66 | .createdOn(event.getCreatedOn()) 67 | .description(event.getDescription()) 68 | .eventDate(event.getEventDate()) 69 | .id(event.getId()) 70 | .paid(event.getPaid()) 71 | .participantLimit(event.getParticipantLimit()) 72 | .publishedOn(event.getPublishedOn()) 73 | .requestModeration(event.getRequestModeration()) 74 | .state(event.getState()) 75 | .views(event.getViews()) 76 | .rate(event.getRate()) 77 | .build(); 78 | } 79 | 80 | public static List eventToPublicListOutDto(List listEvents) { 81 | List eventOutDtoList = new ArrayList<>(); 82 | for (Event event : listEvents) { 83 | eventOutDtoList.add(eventToPublicOutDto(event)); 84 | } 85 | return eventOutDtoList; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/LocationMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.locations.LocationDto; 4 | import ru.practicum.ewm.model.Location; 5 | 6 | public class LocationMapper { 7 | 8 | public static LocationDto locationToDto(Location location) { 9 | return new LocationDto( 10 | location.getLat(), 11 | location.getLon() 12 | ); 13 | } 14 | 15 | public static Location dtoToLocation(LocationDto locationDto) { 16 | return new Location( 17 | null, 18 | locationDto.getLat(), 19 | locationDto.getLon() 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.requests.RequestOutDto; 4 | import ru.practicum.ewm.model.Request; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class RequestMapper { 10 | 11 | 12 | public static RequestOutDto requestToOutDto(Request request) { 13 | return new RequestOutDto( 14 | request.getId(), 15 | request.getCreated(), 16 | request.getEvent().getId(), 17 | request.getRequester().getId(), 18 | request.getStatus() 19 | ); 20 | } 21 | 22 | public static List requestsToListOutDto(List requests) { 23 | List requestOutDtoList = new ArrayList<>(); 24 | for (Request request : requests) { 25 | requestOutDtoList.add(requestToOutDto(request)); 26 | } 27 | return requestOutDtoList; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.users.UserDto; 4 | import ru.practicum.ewm.dto.users.UserPublicDto; 5 | import ru.practicum.ewm.model.User; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class UserMapper { 11 | public static UserDto userToDto(User user) { 12 | return new UserDto( 13 | user.getId(), 14 | user.getName(), 15 | user.getEmail(), 16 | user.getRate()); 17 | } 18 | 19 | public static User dtoToUser(UserDto userDto) { 20 | User user = new User(); 21 | user.setName(userDto.getName()); 22 | user.setEmail(userDto.getEmail()); 23 | return user; 24 | } 25 | 26 | public static List userListToDto(List userList) { 27 | List userDtoList = new ArrayList<>(); 28 | for (User user : userList) { 29 | userDtoList.add(userToDto(user)); 30 | } 31 | return userDtoList; 32 | } 33 | 34 | public static UserPublicDto userToPublicDto(User user) { 35 | return new UserPublicDto(user.getName()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Category.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Table(name = "CATEGORIES", schema = "PUBLIC") 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class Category { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | @Column(unique = true) 21 | private String name; 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Compilation.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.util.List; 7 | 8 | @Entity 9 | @Table(name = "COMPILATIONS", schema = "PUBLIC") 10 | @EqualsAndHashCode(exclude = "events") 11 | @ToString(exclude = "events") 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class Compilation { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | private String title; 21 | private Boolean pinned; 22 | 23 | @ManyToMany 24 | @JoinTable(name = "COMPILATIONS_EVENTS", 25 | joinColumns = @JoinColumn(name = "COMPILATION_ID"), 26 | inverseJoinColumns = @JoinColumn(name = "EVENT_ID")) 27 | private List events; 28 | } 29 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Event.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.*; 4 | import org.hibernate.annotations.CreationTimestamp; 5 | 6 | import javax.persistence.*; 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | 10 | @Entity 11 | @Table(name = "EVENTS", schema = "PUBLIC") 12 | @EqualsAndHashCode(exclude = "compilations") 13 | @Getter 14 | @Setter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Builder 18 | public class Event { 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | @Column(length = 2000) 23 | private String annotation; 24 | @Column(name = "confirmed_requests") 25 | private Integer confirmedRequests; 26 | @CreationTimestamp 27 | @Column(name = "created_on") 28 | private LocalDateTime createdOn; 29 | @Column(name = "description", length = 7000) 30 | private String description; 31 | @Column(name = "event_date") 32 | private LocalDateTime eventDate; 33 | private Boolean paid; 34 | @Column(name = "participant_limit") 35 | private Integer participantLimit; 36 | @Column(name = "published_on") 37 | private LocalDateTime publishedOn; 38 | @Column(name = "request_moderation") 39 | private Boolean requestModeration; 40 | private String title; 41 | private Long views; 42 | @Column(columnDefinition = "INTEGER DEFAULT 0") 43 | @Builder.Default 44 | private Integer rate = 0; 45 | 46 | @ManyToMany 47 | @JoinTable(name = "COMPILATIONS_EVENTS", 48 | joinColumns = @JoinColumn(name = "EVENT_ID"), 49 | inverseJoinColumns = @JoinColumn(name = "COMPILATION_ID")) 50 | private List compilations; 51 | 52 | @Enumerated(EnumType.STRING) 53 | private EventState state; 54 | 55 | @ManyToOne 56 | @JoinColumn(name = "category_id") 57 | private Category category; 58 | 59 | @ManyToOne(cascade = CascadeType.ALL) 60 | @JoinColumn(name = "location_id") 61 | private Location location; 62 | 63 | @ManyToOne 64 | @JoinColumn(name = "initiator_id") 65 | private User initiator; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/EventState.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | public enum EventState { 4 | PENDING, 5 | PUBLISHED, 6 | CANCELED 7 | } 8 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Like.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Table(name = "LIKES", schema = "PUBLIC") 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class Like { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | @Column(name = "USER_ID") 21 | private Long userId; 22 | @Column(name = "EVENT_ID") 23 | private Long eventId; 24 | @Enumerated(EnumType.STRING) 25 | private LikeType type; 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/LikeType.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import java.util.Optional; 4 | 5 | public enum LikeType { 6 | LIKE, 7 | DISLIKE; 8 | 9 | public static Optional from(String stringType) { 10 | for (LikeType type : values()) { 11 | if (type.name().equalsIgnoreCase(stringType)) { 12 | return Optional.of(type); 13 | } 14 | } 15 | return Optional.empty(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Location.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Table(name = "LOCATIONS", schema = "PUBLIC") 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class Location { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | private Float lat; 21 | private Float lon; 22 | } 23 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/Request.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import ru.practicum.ewm.Constants; 9 | 10 | import javax.persistence.*; 11 | import java.time.LocalDateTime; 12 | 13 | @Entity 14 | @Table(name = "REQUESTS", schema = "PUBLIC", uniqueConstraints = {@UniqueConstraint(columnNames = {"EVENT_ID", "REQUESTER_ID"})}) 15 | @Getter 16 | @Setter 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class Request { 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | private Long id; 23 | @ManyToOne(fetch = FetchType.LAZY) 24 | @JoinColumn(name = "REQUESTER_ID") 25 | private User requester; 26 | @JsonFormat(pattern = Constants.DATE_TIME_STRING) 27 | private LocalDateTime created; 28 | @Enumerated(EnumType.STRING) 29 | private RequestState status; 30 | @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 31 | @JoinColumn(name = "EVENT_ID") 32 | private Event event; 33 | } 34 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/RequestState.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | public enum RequestState { 4 | PENDING, 5 | CONFIRMED, 6 | REJECTED, 7 | CANCELED 8 | } 9 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/SortType.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import java.util.Optional; 4 | 5 | public enum SortType { 6 | EVENT_DATE, 7 | VIEWS, 8 | RATE; 9 | 10 | public static Optional from(String stringType) { 11 | for (SortType type : values()) { 12 | if (type.name().equalsIgnoreCase(stringType)) { 13 | return Optional.of(type); 14 | } 15 | } 16 | return Optional.empty(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/model/User.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Table(name = "USERS", schema = "PUBLIC") 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class User { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | private String name; 21 | @Column(name = "email", unique = true) 22 | private String email; 23 | private Float rate; 24 | } 25 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/CategoriesRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import ru.practicum.ewm.model.Category; 5 | 6 | public interface CategoriesRepository extends JpaRepository { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/CompilationsRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import ru.practicum.ewm.model.Compilation; 6 | 7 | import java.util.List; 8 | 9 | public interface CompilationsRepository extends JpaRepository { 10 | List findAlLByPinned(Boolean pinned, Pageable pageable); 11 | } 12 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/EventsRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Modifying; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.transaction.annotation.Transactional; 8 | import ru.practicum.ewm.model.Event; 9 | import ru.practicum.ewm.model.EventState; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public interface EventsRepository extends JpaRepository { 16 | List findAllByInitiatorId(Long userId, Pageable pageable); 17 | 18 | @Query("SELECT e FROM Event e " + 19 | " WHERE e.initiator.id IN :users " + 20 | " AND e.state IN :states " + 21 | " AND e.category.id IN :categories " + 22 | " AND e.eventDate BETWEEN :startDate AND :endDate " 23 | ) 24 | List findAllByUsersAndStatesAndCategories(Long[] users, List states, Long[] categories, LocalDateTime startDate, LocalDateTime endDate, Pageable pageable); 25 | 26 | Boolean existsByCategoryId(Long catId); 27 | 28 | Boolean existsByIdAndState(Long eventId, EventState eventState); 29 | 30 | Optional findByIdAndState(Long eventId, EventState published); 31 | 32 | @Modifying(clearAutomatically = true, flushAutomatically = true) 33 | @Transactional 34 | @Query("UPDATE Event e " + 35 | " SET e.views = e.views + 1 " + 36 | " WHERE e.id = :eventId") 37 | void incrementViews(Long eventId); 38 | 39 | @Query("SELECT e FROM Event e " + 40 | " WHERE e.state = ru.practicum.ewm.model.EventState.PUBLISHED " + 41 | " AND (e.annotation LIKE CONCAT('%',:text,'%') OR e.description LIKE CONCAT('%',:text,'%')) " + 42 | " AND e.category.id IN :categories " + 43 | " AND e.paid = :paid " + 44 | " AND (e.eventDate BETWEEN :rangeStart AND :rangeEnd) " + 45 | " AND (" + 46 | " (:onlyAvailable = true AND e.participantLimit = 0) OR " + 47 | " (:onlyAvailable = true AND e.participantLimit > e.confirmedRequests) OR " + 48 | " (:onlyAvailable = false)" + 49 | ") " 50 | ) 51 | List findAllByParam(String text, 52 | Long[] categories, 53 | Boolean paid, 54 | LocalDateTime rangeStart, 55 | LocalDateTime rangeEnd, 56 | Boolean onlyAvailable, 57 | Pageable pageable); 58 | 59 | 60 | int countByInitiatorId(Long userId); 61 | 62 | @Query("SELECT SUM(e.rate) FROM Event e " + 63 | " WHERE e.initiator.id = :userId" 64 | ) 65 | long sumRateByInitiatorId(Long userId); 66 | 67 | @Modifying(clearAutomatically = true, flushAutomatically = true) 68 | @Transactional 69 | @Query("UPDATE Event e " + 70 | " SET e.rate = e.rate + 1 " + 71 | " WHERE e.id = :eventId") 72 | void incrementRate(Long eventId); 73 | 74 | @Modifying(clearAutomatically = true, flushAutomatically = true) 75 | @Transactional 76 | @Query("UPDATE Event e " + 77 | " SET e.rate = e.rate - 1 " + 78 | " WHERE e.id = :eventId") 79 | void decrementRate(Long eventId); 80 | } 81 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/LikeRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import ru.practicum.ewm.model.Like; 5 | import ru.practicum.ewm.model.LikeType; 6 | 7 | import java.util.Optional; 8 | 9 | public interface LikeRepository extends JpaRepository { 10 | 11 | Optional findByEventIdAndUserId(Long userId, Long eventId); 12 | 13 | Optional findByUserIdAndEventIdAndType(Long userId, Long eventId, LikeType likeType); 14 | } -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/RequestsRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Modifying; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.transaction.annotation.Transactional; 7 | import ru.practicum.ewm.model.Request; 8 | import ru.practicum.ewm.model.RequestState; 9 | 10 | import java.util.List; 11 | 12 | public interface RequestsRepository extends JpaRepository { 13 | List findAllByRequesterId(Long userId); 14 | 15 | @Modifying(clearAutomatically = true, flushAutomatically = true) 16 | @Transactional 17 | @Query("UPDATE Request r " + 18 | " SET r.status = ru.practicum.ewm.model.RequestState.REJECTED " + 19 | " WHERE r.event.id = :eventId AND r.status = ru.practicum.ewm.model.RequestState.PENDING " + 20 | " ") 21 | void rejectAllPendingRequest(Long eventId); 22 | 23 | @Query("SELECT r FROM Request r " + 24 | " JOIN Event e ON r.event.id = e.id " + 25 | " WHERE e.id = :eventId AND e.initiator.id = :userId " 26 | ) 27 | List findAllByInitiatorIdAndEventId(Long userId, Long eventId); 28 | 29 | boolean existsByRequesterIdAndEventIdAndStatus(Long userId, Long eventId, RequestState requestState); 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/repository/UsersRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import ru.practicum.ewm.model.User; 7 | 8 | import java.util.List; 9 | 10 | public interface UsersRepository extends JpaRepository { 11 | @Query( 12 | "SELECT u FROM User as u WHERE u.id IN :ids" 13 | ) 14 | List findAllByIds(Long[] ids, Pageable pageable); 15 | } 16 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/utils/AdminStatsClient.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.utils; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.PropertySource; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.stereotype.Service; 8 | import ru.practicum.ewm.dto.stats.StatInDto; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | @Service 14 | @PropertySource("classpath:application.properties") 15 | public class AdminStatsClient extends BaseClient { 16 | 17 | @Autowired 18 | public AdminStatsClient(@Value("${stats-server.url}") String serverUrl) { 19 | super(serverUrl); 20 | } 21 | 22 | public ResponseEntity getHits(String start, String end, List uris, Boolean unique) { 23 | Map parameters = Map.of( 24 | "start", start, 25 | "end", end, 26 | "uris", uris, 27 | "unique", unique 28 | ); 29 | return get("/?start={start}&end={end}&uris={uris}&unique={unique}", parameters); 30 | } 31 | 32 | public void saveHit(StatInDto statInDto) { 33 | post("/hit", statInDto); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/utils/BaseClient.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.utils; 2 | 3 | import org.springframework.boot.web.client.RestTemplateBuilder; 4 | import org.springframework.http.*; 5 | import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 6 | import org.springframework.lang.Nullable; 7 | import org.springframework.web.client.HttpStatusCodeException; 8 | import org.springframework.web.client.RestTemplate; 9 | import org.springframework.web.util.DefaultUriBuilderFactory; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class BaseClient { 15 | protected final RestTemplate rest; 16 | 17 | public BaseClient(String serverUrl) { 18 | rest = new RestTemplateBuilder() 19 | .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl)) 20 | .requestFactory(HttpComponentsClientHttpRequestFactory::new) 21 | .build(); 22 | } 23 | 24 | private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { 25 | if (response.getStatusCode().is2xxSuccessful()) { 26 | return response; 27 | } 28 | 29 | ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); 30 | 31 | if (response.hasBody()) { 32 | return responseBuilder.body(response.getBody()); 33 | } 34 | 35 | return responseBuilder.build(); 36 | } 37 | 38 | protected ResponseEntity get(String path, @Nullable Map parameters) { 39 | return makeAndSendRequest(HttpMethod.GET, path, parameters, null); 40 | } 41 | 42 | protected ResponseEntity post(String path, T body) { 43 | return post(path, null, body); 44 | } 45 | 46 | protected ResponseEntity post(String path, @Nullable Map parameters, T body) { 47 | return makeAndSendRequest(HttpMethod.POST, path, parameters, body); 48 | } 49 | 50 | private ResponseEntity makeAndSendRequest(HttpMethod method, String path, @Nullable Map parameters, @Nullable T body) { 51 | HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders()); 52 | 53 | ResponseEntity ewmServerResponse; 54 | try { 55 | if (parameters != null) { 56 | ewmServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); 57 | } else { 58 | ewmServerResponse = rest.exchange(path, method, requestEntity, Object.class); 59 | } 60 | } catch (HttpStatusCodeException e) { 61 | return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); 62 | } 63 | return prepareGatewayResponse(ewmServerResponse); 64 | } 65 | 66 | private HttpHeaders defaultHeaders() { 67 | HttpHeaders headers = new HttpHeaders(); 68 | headers.setContentType(MediaType.APPLICATION_JSON); 69 | headers.setAccept(List.of(MediaType.APPLICATION_JSON)); 70 | return headers; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /main/src/main/java/ru/practicum/ewm/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.utils; 2 | 3 | import ru.practicum.ewm.dto.events.EventInDto; 4 | import ru.practicum.ewm.exception.CategoryNotFoundException; 5 | import ru.practicum.ewm.model.Event; 6 | import ru.practicum.ewm.repository.CategoriesRepository; 7 | 8 | import java.security.InvalidParameterException; 9 | import java.time.LocalDateTime; 10 | 11 | public class Utils { 12 | public static void checkTimeBeforeOrThrow(LocalDateTime eventDate, int hours) { 13 | if (eventDate.isBefore(LocalDateTime.now().plusHours(hours))) { 14 | throw new InvalidParameterException("Event Time must be, late then " + hours + " hours."); 15 | } 16 | } 17 | 18 | public static void setNotNullParamToEntity(EventInDto eventInDto, Event event, CategoriesRepository categoriesRepository) 19 | throws CategoryNotFoundException { 20 | if (eventInDto.getCategory() != null) { 21 | if (!categoriesRepository.existsById(eventInDto.getCategory())) { 22 | throw new CategoryNotFoundException("Category ID not found."); 23 | } 24 | event.setCategory(categoriesRepository.getReferenceById(eventInDto.getCategory())); 25 | } 26 | if (eventInDto.getAnnotation() != null) { 27 | event.setAnnotation(eventInDto.getAnnotation()); 28 | } 29 | if (eventInDto.getDescription() != null) { 30 | event.setDescription(eventInDto.getDescription()); 31 | } 32 | if (eventInDto.getTitle() != null) { 33 | event.setTitle(eventInDto.getTitle()); 34 | } 35 | if (eventInDto.getPaid() != null) { 36 | event.setPaid(eventInDto.getPaid()); 37 | } 38 | if (eventInDto.getParticipantLimit() != null) { 39 | event.setParticipantLimit(eventInDto.getParticipantLimit()); 40 | } 41 | if (eventInDto.getRequestModeration() != null) { 42 | event.setRequestModeration(eventInDto.getRequestModeration()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /main/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | stats-server.url=http://localhost:9090 3 | spring.sql.init.mode=always 4 | spring.jpa.hibernate.ddl-auto=none 5 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect 6 | spring.jpa.properties.hibernate.format_sql=true 7 | # docker 8 | #--- 9 | spring.datasource.driver-class-name=org.postgresql.Driver 10 | spring.datasource.url=${SPRING_DATASOURCE_URL} 11 | spring.datasource.username=${POSTGRES_USER} 12 | spring.datasource.password=${POSTGRES_PASSWORD} 13 | #--- 14 | spring.config.activate.on-profile=ci,test 15 | spring.datasource.driverClassName=org.h2.Driver 16 | spring.datasource.url=jdbc:h2:mem:main 17 | spring.datasource.username=test 18 | spring.datasource.password=test 19 | # sql & log show debug 20 | #logging.level.ru.practicum=DEBUG 21 | #logging.level.org.springframework.web.client.RestTemplate=DEBUG 22 | #logging.level.org.springframework.web=DEBUG 23 | #logging.level.org.hibernate=TRACE 24 | #spring.jpa.show-sql=true -------------------------------------------------------------------------------- /main/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS compilations cascade; 2 | DROP TABLE IF EXISTS requests cascade; 3 | DROP TABLE IF EXISTS events cascade; 4 | DROP TABLE IF EXISTS users cascade; 5 | DROP TABLE IF EXISTS categories cascade; 6 | DROP TABLE IF EXISTS locations cascade; 7 | DROP TABLE IF EXISTS likes cascade; 8 | 9 | CREATE TABLE IF NOT EXISTS users 10 | ( 11 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 12 | name VARCHAR(250) NOT NULL, 13 | email VARCHAR(100) UNIQUE, 14 | rate NUMERIC(10, 2) DEFAULT 0, 15 | CONSTRAINT UQ_USER_EMAIL UNIQUE (email) 16 | ); 17 | 18 | CREATE TABLE IF NOT EXISTS categories 19 | ( 20 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 21 | name VARCHAR(200), 22 | CONSTRAINT UQ_NAME UNIQUE (name) 23 | ); 24 | 25 | CREATE TABLE IF NOT EXISTS locations 26 | ( 27 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 28 | lat FLOAT, 29 | lon FLOAT 30 | ); 31 | 32 | 33 | CREATE TABLE IF NOT EXISTS events 34 | ( 35 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 36 | annotation TEXT 37 | CONSTRAINT annotation_length CHECK (char_length(annotation) >= 20 AND char_length(annotation) <= 2000), 38 | category_id BIGINT NOT NULL 39 | CONSTRAINT events_categories_fkey REFERENCES categories, 40 | confirmed_requests INTEGER, 41 | created_on TIMESTAMP WITHOUT TIME ZONE, 42 | description TEXT, 43 | CONSTRAINT description_length CHECK (char_length(description) >= 20 AND char_length(description) <= 7000), 44 | event_date TIMESTAMP WITHOUT TIME ZONE, 45 | initiator_id BIGINT NOT NULL 46 | CONSTRAINT events_users_fkey REFERENCES users, 47 | location_id BIGINT NOT NULL 48 | CONSTRAINT events_locations_fkey REFERENCES locations, 49 | paid BOOLEAN default false, 50 | participant_limit INTEGER DEFAULT 0, 51 | published_on TIMESTAMP WITHOUT TIME ZONE, 52 | request_moderation BOOLEAN DEFAULT true, 53 | state VARCHAR(15), 54 | title VARCHAR(120) 55 | CONSTRAINT title_length CHECK (char_length(title) >= 3), 56 | views BIGINT DEFAULT 0, 57 | rate INTEGER DEFAULT 0 58 | ); 59 | 60 | CREATE TABLE IF NOT EXISTS compilations 61 | ( 62 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 63 | title VARCHAR(120), 64 | pinned BOOLEAN default false 65 | ); 66 | 67 | CREATE TABLE IF NOT EXISTS compilations_events 68 | ( 69 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 70 | event_id BIGINT REFERENCES events (id), 71 | compilation_id BIGINT REFERENCES compilations (id) 72 | ); 73 | 74 | CREATE TABLE IF NOT EXISTS likes 75 | ( 76 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 77 | user_id BIGINT REFERENCES users (id), 78 | event_id BIGINT REFERENCES events (id), 79 | type VARCHAR(7) 80 | ); 81 | 82 | CREATE TABLE IF NOT EXISTS requests 83 | ( 84 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 85 | requester_id BIGINT REFERENCES users (id), 86 | created TIMESTAMP WITHOUT TIME ZONE, 87 | status VARCHAR(15), 88 | event_id BIGINT REFERENCES events (id), 89 | UNIQUE (event_id, requester_id) 90 | ); 91 | -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/MainAPITest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class MainAPITest { 6 | @Test 7 | void contextLoads() { 8 | } 9 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.CsvSource; 7 | import ru.practicum.ewm.utils.Utils; 8 | 9 | import java.security.InvalidParameterException; 10 | import java.time.LocalDateTime; 11 | 12 | class UtilsTest { 13 | @DisplayName("Check Before Time or Throw") 14 | @ParameterizedTest 15 | @CsvSource({ 16 | "62, 1, OK", 17 | "30, 1, ERROR", 18 | "0, 1, ERROR", 19 | "-30, 1, ERROR" 20 | }) 21 | void checkTimeBeforeOrThrow(long timeToEventInMinutes, int timeLimitInHours, String expectedMessage) { 22 | LocalDateTime eventDate = LocalDateTime.now().plusMinutes(timeToEventInMinutes); 23 | if ("ERROR".equals(expectedMessage)) { 24 | 25 | final InvalidParameterException exception = Assertions.assertThrows( 26 | InvalidParameterException.class, 27 | () -> Utils.checkTimeBeforeOrThrow(eventDate, timeLimitInHours) 28 | ); 29 | Assertions.assertEquals("Event Time must be, late then 1 hours.", exception.getMessage()); 30 | } else { 31 | Utils.checkTimeBeforeOrThrow(eventDate, timeLimitInHours); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/categories/CategoryFullDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.categories; 2 | 3 | import org.assertj.core.api.AssertionsForClassTypes; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 11 | import org.springframework.boot.test.json.JacksonTester; 12 | import ru.practicum.ewm.dto.categories.CategoryFullDto; 13 | 14 | import javax.validation.ConstraintViolation; 15 | import javax.validation.Validation; 16 | import javax.validation.Validator; 17 | import javax.validation.ValidatorFactory; 18 | import java.util.Set; 19 | 20 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 21 | 22 | @JsonTest 23 | class CategoryFullDtoTest { 24 | private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 25 | private final Validator validator = factory.getValidator(); 26 | 27 | @Autowired 28 | private JacksonTester json; 29 | 30 | @Test 31 | void testSerialize() throws Exception { 32 | var dto = new CategoryFullDto( 33 | 3L, 34 | "Name categories" 35 | ); 36 | 37 | var result = json.write(dto); 38 | assertThat(result).hasJsonPath("$.id"); 39 | assertThat(result).hasJsonPath("$.name"); 40 | 41 | assertThat(result).extractingJsonPathNumberValue("$.id").isEqualTo(dto.getId().intValue()); 42 | assertThat(result).extractingJsonPathStringValue("$.name").isEqualTo(dto.getName()); 43 | } 44 | 45 | @DisplayName("Check Blank") 46 | @ParameterizedTest 47 | @CsvSource({ 48 | "null, 1, NotBlank.message", 49 | "'', 1, NotBlank.message", 50 | "text, 0, OK", 51 | }) 52 | void textNotBlank(String text, int expectSize, String expectedMessage) { 53 | if ("null".equals(text)) { 54 | text = null; 55 | } 56 | var dto = new CategoryFullDto(1L, text); 57 | Set> violations = validator.validate(dto); 58 | Assertions.assertEquals(expectSize, violations.size()); 59 | 60 | if (!violations.isEmpty()) { 61 | AssertionsForClassTypes.assertThat(violations.iterator().next().getMessageTemplate()) 62 | .contains(expectedMessage); 63 | } 64 | } 65 | 66 | @DisplayName("Check Positive") 67 | @ParameterizedTest 68 | @CsvSource({ 69 | "-1, 1, Positive.message", 70 | "0, 1, Positive.message", 71 | "1, 0, OK", 72 | }) 73 | void textNotBlank(long id, int expectSize, String expectedMessage) { 74 | var dto = new CategoryFullDto(id, "text"); 75 | Set> violations = validator.validate(dto); 76 | Assertions.assertEquals(expectSize, violations.size()); 77 | 78 | if (!violations.isEmpty()) { 79 | AssertionsForClassTypes.assertThat(violations.iterator().next().getMessageTemplate()) 80 | .contains(expectedMessage); 81 | } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/categories/CategoryInDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.categories; 2 | 3 | import org.assertj.core.api.AssertionsForClassTypes; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 11 | import org.springframework.boot.test.json.JacksonTester; 12 | import ru.practicum.ewm.dto.categories.CategoryInDto; 13 | 14 | import javax.validation.ConstraintViolation; 15 | import javax.validation.Validation; 16 | import javax.validation.Validator; 17 | import javax.validation.ValidatorFactory; 18 | import java.util.Set; 19 | 20 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 21 | 22 | @JsonTest 23 | class CategoryInDtoTest { 24 | private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 25 | private final Validator validator = factory.getValidator(); 26 | 27 | @Autowired 28 | private JacksonTester json; 29 | 30 | @Test 31 | void testSerialize() throws Exception { 32 | var dto = new CategoryInDto( 33 | "Name categories" 34 | ); 35 | 36 | var result = json.write(dto); 37 | assertThat(result).hasJsonPath("$.name"); 38 | 39 | assertThat(result).extractingJsonPathStringValue("$.name").isEqualTo(dto.getName()); 40 | } 41 | 42 | @DisplayName("Check Blank") 43 | @ParameterizedTest 44 | @CsvSource({ 45 | "null, 1, NotBlank.message", 46 | "'', 1, NotBlank.message", 47 | "text, 0, OK", 48 | }) 49 | void textNotBlank(String text, int expectSize, String expectedMessage) { 50 | if ("null".equals(text)) { 51 | text = null; 52 | } 53 | var dto = new CategoryInDto(text); 54 | Set> violations = validator.validate(dto); 55 | Assertions.assertEquals(expectSize, violations.size()); 56 | 57 | if (!violations.isEmpty()) { 58 | AssertionsForClassTypes.assertThat(violations.iterator().next().getMessageTemplate()) 59 | .contains(expectedMessage); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/compilations/CompilationInDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.compilations; 2 | 3 | import org.assertj.core.api.AssertionsForClassTypes; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 11 | import org.springframework.boot.test.json.JacksonTester; 12 | import ru.practicum.ewm.dto.compilations.CompilationInDto; 13 | 14 | import javax.validation.ConstraintViolation; 15 | import javax.validation.Validation; 16 | import javax.validation.Validator; 17 | import javax.validation.ValidatorFactory; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Set; 21 | 22 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 23 | 24 | @JsonTest 25 | class CompilationInDtoTest { 26 | private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 27 | private final Validator validator = factory.getValidator(); 28 | 29 | @Autowired 30 | private JacksonTester json; 31 | 32 | @Test 33 | void testSerialize() throws Exception { 34 | var dto = new CompilationInDto( 35 | "title 1", 36 | new ArrayList<>(), 37 | true 38 | ); 39 | 40 | var result = json.write(dto); 41 | assertThat(result).hasJsonPath("$.title"); 42 | assertThat(result).hasJsonPath("$.events"); 43 | assertThat(result).hasJsonPath("$.pinned"); 44 | 45 | assertThat(result).extractingJsonPathStringValue("$.title").isEqualTo(dto.getTitle()); 46 | assertThat(result).extractingJsonPathBooleanValue("$.pinned").isEqualTo(dto.getPinned()); 47 | } 48 | 49 | @DisplayName("Check Blank") 50 | @ParameterizedTest 51 | @CsvSource({ 52 | "null, 1, NotBlank.message", 53 | "'', 1, NotBlank.message", 54 | "text, 0, OK", 55 | }) 56 | void textNotBlank(String text, int expectSize, String expectedMessage) { 57 | if ("null".equals(text)) { 58 | text = null; 59 | } 60 | var dto = new CompilationInDto(text, List.of(), true); 61 | Set> violations = validator.validate(dto); 62 | Assertions.assertEquals(expectSize, violations.size()); 63 | 64 | if (!violations.isEmpty()) { 65 | AssertionsForClassTypes.assertThat(violations.iterator().next().getMessageTemplate()) 66 | .contains(expectedMessage); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/compilations/CompilationOutDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.compilations; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import ru.practicum.ewm.dto.compilations.CompilationOutDto; 8 | 9 | import java.util.ArrayList; 10 | 11 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 12 | 13 | @JsonTest 14 | class CompilationOutDtoTest { 15 | @Autowired 16 | private JacksonTester json; 17 | 18 | @Test 19 | void testSerialize() throws Exception { 20 | var dto = new CompilationOutDto( 21 | 1L, 22 | "title 1", 23 | new ArrayList<>(), 24 | true 25 | ); 26 | 27 | var result = json.write(dto); 28 | assertThat(result).hasJsonPath("$.id"); 29 | assertThat(result).hasJsonPath("$.title"); 30 | assertThat(result).hasJsonPath("$.events"); 31 | assertThat(result).hasJsonPath("$.pinned"); 32 | 33 | assertThat(result).extractingJsonPathNumberValue("$.id").isEqualTo(dto.getId().intValue()); 34 | assertThat(result).extractingJsonPathStringValue("$.title").isEqualTo(dto.getTitle()); 35 | assertThat(result).extractingJsonPathBooleanValue("$.pinned").isEqualTo(dto.getPinned()); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/events/EventInDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.events; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import ru.practicum.ewm.dto.events.EventInDto; 8 | 9 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 10 | 11 | @JsonTest 12 | class EventInDtoTest { 13 | @Autowired 14 | private JacksonTester json; 15 | 16 | @Test 17 | void testSerialize() throws Exception { 18 | var dto = new EventInDto( 19 | "annotation 1234566778891234567898765432", 20 | 1L, 21 | "description12345678909876544332122145678765432", 22 | null, 23 | "title", 24 | "2022-10-21 20:48:11", 25 | true, 26 | 10, 27 | false 28 | ); 29 | 30 | var result = json.write(dto); 31 | assertThat(result).hasJsonPath("$.annotation"); 32 | assertThat(result).hasJsonPath("$.category"); 33 | assertThat(result).hasJsonPath("$.description"); 34 | assertThat(result).hasJsonPath("$.location"); 35 | assertThat(result).hasJsonPath("$.title"); 36 | assertThat(result).hasJsonPath("$.eventDate"); 37 | assertThat(result).hasJsonPath("$.paid"); 38 | assertThat(result).hasJsonPath("$.participantLimit"); 39 | assertThat(result).hasJsonPath("$.requestModeration"); 40 | 41 | assertThat(result).extractingJsonPathStringValue("$.annotation").isEqualTo(dto.getAnnotation()); 42 | assertThat(result).extractingJsonPathStringValue("$.description").isEqualTo(dto.getDescription()); 43 | assertThat(result).extractingJsonPathStringValue("$.title").isEqualTo(dto.getTitle()); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/events/EventOutDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.events; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import ru.practicum.ewm.dto.events.EventOutDto; 8 | import ru.practicum.ewm.model.EventState; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 13 | 14 | @JsonTest 15 | class EventOutDtoTest { 16 | @Autowired 17 | private JacksonTester json; 18 | 19 | @Test 20 | void testSerialize() throws Exception { 21 | var dto = new EventOutDto( 22 | "annotation 1234566778891234567898765432", 23 | null, 24 | null, 25 | null, 26 | "title", 27 | 0, 28 | LocalDateTime.now(), 29 | "description12345678909876544332122145678765432", 30 | LocalDateTime.now(), 31 | 1L, 32 | true, 33 | 10, 34 | LocalDateTime.now(), 35 | false, 36 | EventState.PENDING, 37 | 0L, 38 | 5 39 | ); 40 | 41 | var result = json.write(dto); 42 | assertThat(result).hasJsonPath("$.annotation"); 43 | assertThat(result).hasJsonPath("$.category"); 44 | assertThat(result).hasJsonPath("$.initiator"); 45 | assertThat(result).hasJsonPath("$.location"); 46 | assertThat(result).hasJsonPath("$.title"); 47 | assertThat(result).hasJsonPath("$.confirmedRequests"); 48 | assertThat(result).hasJsonPath("$.createdOn"); 49 | assertThat(result).hasJsonPath("$.description"); 50 | assertThat(result).hasJsonPath("$.eventDate"); 51 | assertThat(result).hasJsonPath("$.id"); 52 | assertThat(result).hasJsonPath("$.paid"); 53 | assertThat(result).hasJsonPath("$.participantLimit"); 54 | assertThat(result).hasJsonPath("$.publishedOn"); 55 | assertThat(result).hasJsonPath("$.requestModeration"); 56 | assertThat(result).hasJsonPath("$.state"); 57 | assertThat(result).hasJsonPath("$.views"); 58 | assertThat(result).hasJsonPath("$.rate"); 59 | 60 | assertThat(result).extractingJsonPathStringValue("$.annotation").isEqualTo(dto.getAnnotation()); 61 | assertThat(result).extractingJsonPathStringValue("$.description").isEqualTo(dto.getDescription()); 62 | assertThat(result).extractingJsonPathStringValue("$.title").isEqualTo(dto.getTitle()); 63 | } 64 | 65 | @Test 66 | void testBuilderSerialize() throws Exception { 67 | var dto = EventOutDto.builder() 68 | .annotation("annotation 1234566778891234567898765432") 69 | .title("title") 70 | .description("description12345678909876544332122145678765432") 71 | .build(); 72 | 73 | var result = json.write(dto); 74 | assertThat(result).hasJsonPath("$.annotation"); 75 | assertThat(result).hasJsonPath("$.title"); 76 | assertThat(result).hasJsonPath("$.description"); 77 | 78 | assertThat(result).extractingJsonPathStringValue("$.annotation").isEqualTo(dto.getAnnotation()); 79 | assertThat(result).extractingJsonPathStringValue("$.description").isEqualTo(dto.getDescription()); 80 | assertThat(result).extractingJsonPathStringValue("$.title").isEqualTo(dto.getTitle()); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/locations/LocationDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.locations; 2 | 3 | import org.assertj.core.api.AssertionsForClassTypes; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 7 | import org.springframework.boot.test.json.JacksonTester; 8 | import ru.practicum.ewm.dto.locations.LocationDto; 9 | 10 | import static org.assertj.core.api.AssertionsForClassTypes.within; 11 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 12 | 13 | @JsonTest 14 | class LocationDtoTest { 15 | @Autowired 16 | private JacksonTester json; 17 | 18 | @Test 19 | void testSerialize() throws Exception { 20 | var dto = new LocationDto( 21 | 1.11f, 22 | 2.22f 23 | ); 24 | 25 | var result = json.write(dto); 26 | assertThat(result).hasJsonPath("$.lat"); 27 | assertThat(result).hasJsonPath("$.lon"); 28 | 29 | assertThat(result).extractingJsonPathNumberValue("$.lat") 30 | .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(dto.getLat(), within(0.01f))); 31 | assertThat(result).extractingJsonPathNumberValue("$.lon") 32 | .satisfies((number) -> AssertionsForClassTypes.assertThat(number.floatValue()).isCloseTo(dto.getLon(), within(0.01f))); 33 | } 34 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/requests/RequestOutDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.requests; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import ru.practicum.ewm.dto.requests.RequestOutDto; 8 | import ru.practicum.ewm.model.RequestState; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 13 | 14 | @JsonTest 15 | class RequestOutDtoTest { 16 | @Autowired 17 | private JacksonTester json; 18 | 19 | @Test 20 | void testSerialize() throws Exception { 21 | var dto = new RequestOutDto( 22 | 2L, 23 | LocalDateTime.now(), 24 | null, 25 | null, 26 | RequestState.PENDING 27 | ); 28 | 29 | var result = json.write(dto); 30 | assertThat(result).hasJsonPath("$.id"); 31 | assertThat(result).hasJsonPath("$.created"); 32 | assertThat(result).hasJsonPath("$.event"); 33 | assertThat(result).hasJsonPath("$.requester"); 34 | assertThat(result).hasJsonPath("$.status"); 35 | } 36 | } -------------------------------------------------------------------------------- /main/src/test/java/ru/practicum/ewm/model/dto/users/UserDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model.dto.users; 2 | 3 | import org.assertj.core.api.AssertionsForClassTypes; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.CsvSource; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 11 | import org.springframework.boot.test.json.JacksonTester; 12 | import ru.practicum.ewm.dto.users.UserDto; 13 | 14 | import javax.validation.ConstraintViolation; 15 | import javax.validation.Validation; 16 | import javax.validation.Validator; 17 | import javax.validation.ValidatorFactory; 18 | import java.util.Set; 19 | 20 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 21 | 22 | @JsonTest 23 | class UserDtoTest { 24 | private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 25 | private final Validator validator = factory.getValidator(); 26 | @Autowired 27 | private JacksonTester json; 28 | 29 | @Test 30 | void testSerialize() throws Exception { 31 | var dto = new UserDto( 32 | 1L, 33 | "User name", 34 | "email@mail.com", 35 | 5.0F 36 | ); 37 | 38 | var result = json.write(dto); 39 | 40 | assertThat(result).hasJsonPath("$.id"); 41 | assertThat(result).hasJsonPath("$.name"); 42 | assertThat(result).hasJsonPath("$.email"); 43 | assertThat(result).hasJsonPath("$.rate"); 44 | 45 | assertThat(result).extractingJsonPathNumberValue("$.id").isEqualTo(dto.getId().intValue()); 46 | assertThat(result).extractingJsonPathStringValue("$.name").isEqualTo(dto.getName()); 47 | assertThat(result).extractingJsonPathStringValue("$.email").isEqualTo(dto.getEmail()); 48 | } 49 | 50 | @DisplayName("Email validation") 51 | @ParameterizedTest 52 | @CsvSource({ 53 | "email, 1, Email.message", 54 | "@email, 1, Email.message", 55 | "email@, 1, Email.message", 56 | "email@@dd, 1, Email.message", 57 | "'ema il@dd', 1, Email.message", 58 | "'name@email.com', 0, OK" 59 | }) 60 | void emailValidation(String email, int expectSize, String expectedMessage) { 61 | if ("null".equals(email)) { 62 | email = null; 63 | } 64 | var dto = new UserDto(); 65 | dto.setEmail(email); 66 | Set> violations = validator.validate(dto); 67 | Assertions.assertEquals(expectSize, violations.size()); 68 | 69 | if (!violations.isEmpty()) { 70 | AssertionsForClassTypes.assertThat(violations.iterator().next().getMessageTemplate()) 71 | .contains(expectedMessage); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.7.5 10 | 11 | 12 | 13 | Explore With Me 14 | 15 | ru.practicum 16 | explore-with-me 17 | 0.0.1-SNAPSHOT 18 | pom 19 | 20 | 21 | 11 22 | 11 23 | 11 24 | UTF-8 25 | 26 | 27 | 28 | 29 | stats 30 | main 31 | common 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-surefire-plugin 40 | 41 | 42 | test 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-checkstyle-plugin 49 | 3.1.2 50 | 51 | checkstyle.xml 52 | true 53 | true 54 | true 55 | 56 | 57 | 58 | 59 | check 60 | 61 | compile 62 | 63 | 64 | 65 | 66 | com.puppycrawl.tools 67 | checkstyle 68 | 10.3 69 | 70 | 71 | 72 | 73 | com.github.spotbugs 74 | spotbugs-maven-plugin 75 | 4.7.0.0 76 | 77 | Max 78 | High 79 | 80 | 81 | 82 | 83 | check 84 | 85 | 86 | 87 | 88 | 89 | org.jacoco 90 | jacoco-maven-plugin 91 | 0.8.8 92 | 93 | file 94 | 95 | 96 | 97 | jacoco-initialize 98 | 99 | prepare-agent 100 | 101 | 102 | 103 | jacoco-check 104 | 105 | check 106 | 107 | 108 | 109 | 110 | BUNDLE 111 | 112 | 113 | INSTRUCTION 114 | COVEREDRATIO 115 | 0.01 116 | 117 | 118 | LINE 119 | COVEREDRATIO 120 | 0.2 121 | 122 | 123 | BRANCH 124 | COVEREDRATIO 125 | 0.2 126 | 127 | 128 | COMPLEXITY 129 | COVEREDRATIO 130 | 0.2 131 | 132 | 133 | METHOD 134 | COVEREDRATIO 135 | 0.2 136 | 137 | 138 | CLASS 139 | MISSEDCOUNT 140 | 1 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | jacoco-site 149 | install 150 | 151 | report 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | check 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-checkstyle-plugin 167 | 3.1.2 168 | 169 | 170 | com.github.spotbugs 171 | spotbugs-maven-plugin 172 | 4.7.1.1 173 | 174 | 175 | 176 | 177 | 178 | 179 | com.github.spotbugs 180 | spotbugs-maven-plugin 181 | 4.7.1.1 182 | 183 | 184 | 185 | 186 | 187 | coverage 188 | 189 | 190 | 191 | org.jacoco 192 | jacoco-maven-plugin 193 | 0.8.8 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /stats/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:11-alpine-jdk 2 | COPY target/ewm-stats-server*.jar ewm_stats_server.jar 3 | ENTRYPOINT ["java","-jar","/ewm_stats_server.jar"] 4 | -------------------------------------------------------------------------------- /stats/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | explore-with-me 7 | ru.practicum 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | ewm-stats-server 13 | 14 | 15 | 11 16 | 11 17 | UTF-8 18 | 19 | 20 | 21 | 22 | ru.practicum 23 | common 24 | 0.0.1-SNAPSHOT 25 | 26 | 27 | org.projectlombok 28 | lombok 29 | true 30 | 31 | 32 | 33 | com.h2database 34 | h2 35 | runtime 36 | 37 | 38 | 39 | com.google.code.findbugs 40 | annotations 41 | 3.0.1 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-web 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-actuator 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-validation 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-configuration-processor 60 | true 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-test 65 | test 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-autoconfigure 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-data-jpa 74 | 75 | 76 | org.postgresql 77 | postgresql 78 | 42.5.0 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.MethodArgumentNotValidException; 5 | import org.springframework.web.bind.annotation.ExceptionHandler; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | import ru.practicum.ewm.controller.StatsController; 9 | 10 | import java.nio.file.AccessDeniedException; 11 | import java.security.InvalidParameterException; 12 | 13 | @RestControllerAdvice(assignableTypes = { 14 | StatsController.class}) 15 | public class ErrorHandler { 16 | private ErrorResponse errorResponse; 17 | 18 | @ExceptionHandler({ 19 | MethodArgumentNotValidException.class, 20 | InvalidParameterException.class 21 | }) 22 | @ResponseStatus(HttpStatus.BAD_REQUEST) 23 | public ErrorResponse handleBadRequest(final Exception e) { 24 | return new ErrorResponse("Ошибка валидации данных: " + e.getMessage()); 25 | } 26 | 27 | @ExceptionHandler({ 28 | IllegalArgumentException.class 29 | }) 30 | @ResponseStatus(HttpStatus.BAD_REQUEST) 31 | public ErrorResponse handleBookingBadRequest(final Exception e) { 32 | return new ErrorResponse(e.getMessage()); 33 | } 34 | 35 | @ExceptionHandler({ 36 | AccessDeniedException.class}) 37 | @ResponseStatus(HttpStatus.FORBIDDEN) 38 | public ErrorResponse handleForbidden(final Exception e) { 39 | return new ErrorResponse(e.getMessage()); 40 | } 41 | 42 | 43 | @ExceptionHandler 44 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 45 | public ErrorResponse handleAllError(final Throwable e) { 46 | return new ErrorResponse("Произошла непредвиденная ошибка." + e.getMessage()); 47 | } 48 | 49 | private static class ErrorResponse { 50 | private final String error; 51 | 52 | public ErrorResponse(String error) { 53 | this.error = error; 54 | } 55 | 56 | public String getError() { 57 | return error; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/StatsAPI.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | import java.util.Collections; 7 | 8 | 9 | @SpringBootApplication 10 | public class StatsAPI { 11 | private static final String port = "9090"; 12 | 13 | public static void main(String[] args) { 14 | SpringApplication app = new SpringApplication(StatsAPI.class); 15 | app.setDefaultProperties(Collections.singletonMap("server.port", port)); 16 | app.run(args); 17 | } 18 | } -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/controller/StatsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.validation.annotation.Validated; 6 | import org.springframework.web.bind.annotation.*; 7 | import ru.practicum.ewm.Constants; 8 | import ru.practicum.ewm.dto.stats.StatInDto; 9 | import ru.practicum.ewm.dto.stats.StatOutDto; 10 | import ru.practicum.ewm.model.Stat; 11 | import ru.practicum.ewm.service.StatsService; 12 | 13 | import javax.validation.Valid; 14 | import javax.validation.constraints.NotNull; 15 | import javax.validation.constraints.Positive; 16 | import javax.validation.constraints.PositiveOrZero; 17 | import java.util.List; 18 | 19 | @RestController 20 | @RequiredArgsConstructor 21 | @Validated 22 | @Slf4j 23 | public class StatsController { 24 | private final StatsService statsService; 25 | 26 | @PostMapping("/hit") 27 | public void saveHit(@Valid @RequestBody StatInDto statInDto) { 28 | log.info("Stats saveHit: {}", statInDto); 29 | statsService.saveHit(statInDto); 30 | } 31 | 32 | @GetMapping("/stats") 33 | public List getHits(@NotNull @RequestParam(name = "start") String start, 34 | @NotNull @RequestParam(name = "end") String end, 35 | @Valid 36 | @RequestParam(name = "uris", defaultValue = "", required = false) List uris, 37 | @RequestParam(name = "unique", defaultValue = "false") Boolean unique) { 38 | log.info("Stats getHits: {},{},{},{}", start, end, uris, unique); 39 | return statsService.getHits(start, end, uris, unique); 40 | } 41 | 42 | @GetMapping("/log") 43 | public List getAllHits(@PositiveOrZero 44 | @RequestParam(name = "from", defaultValue = "0") Integer from, 45 | @Positive 46 | @RequestParam(name = "size", defaultValue = Constants.PAGE_SIZE_STRING) Integer size) { 47 | log.info("Stats getAllHits: {},{}", from, size); 48 | return statsService.getAllHits(from, size); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/mapper/StatMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import ru.practicum.ewm.dto.stats.StatInDto; 4 | import ru.practicum.ewm.model.Stat; 5 | 6 | public class StatMapper { 7 | public static Stat dtoToStat(StatInDto statInDto) { 8 | Stat stat = new Stat(); 9 | 10 | stat.setApp(statInDto.getApp()); 11 | stat.setUri(statInDto.getUri()); 12 | stat.setIp(statInDto.getIp()); 13 | stat.setTimestamp(statInDto.getTimestamp()); 14 | 15 | return stat; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/model/Stat.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | import java.time.LocalDateTime; 10 | 11 | @Entity 12 | @Table(name = "stats", schema = "public", indexes = {@Index(name = "APP_URI_INDEX", columnList = "APP, URI, IP")}) 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class Stat { 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | private String app; 22 | private String uri; 23 | private String ip; 24 | private LocalDateTime timestamp; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/repository/StatsRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | import ru.practicum.ewm.dto.stats.StatOutDto; 6 | import ru.practicum.ewm.model.Stat; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | 11 | public interface StatsRepository extends JpaRepository { 12 | @Query("SELECT new ru.practicum.ewm.dto.stats.StatOutDto(s.app, s.uri, count(s.id)) " + 13 | " FROM Stat as s " + 14 | " WHERE s.timestamp BETWEEN :start AND :end " + 15 | " GROUP BY s.app, s.uri " 16 | ) 17 | List countByTimestamp(LocalDateTime start, LocalDateTime end); 18 | 19 | @Query("SELECT new ru.practicum.ewm.dto.stats.StatOutDto(s.app, s.uri, count(s.id)) " + 20 | " FROM Stat as s " + 21 | " WHERE s.timestamp BETWEEN :start AND :end " + 22 | " GROUP BY s.app, s.uri, s.ip " 23 | ) 24 | List countByTimestampUniqueIp(LocalDateTime start, LocalDateTime end); 25 | 26 | @Query("SELECT new ru.practicum.ewm.dto.stats.StatOutDto(s.app, s.uri, count(s.id)) " + 27 | " FROM Stat as s " + 28 | " WHERE s.uri IN :uris AND s.timestamp BETWEEN :start AND :end " + 29 | " GROUP BY s.app, s.uri " 30 | ) 31 | List countByTimestampAndList(LocalDateTime start, LocalDateTime end, List uris); 32 | 33 | @Query("SELECT new ru.practicum.ewm.dto.stats.StatOutDto(s.app, s.uri, count(s.id)) " + 34 | " FROM Stat as s " + 35 | " WHERE s.uri IN :uris AND s.timestamp BETWEEN :start AND :end " + 36 | " GROUP BY s.app, s.uri, s.ip " 37 | ) 38 | List countByTimestampAndListUniqueIp(LocalDateTime start, LocalDateTime end, List uris); 39 | } 40 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/service/StatsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.service; 2 | 3 | import ru.practicum.ewm.dto.stats.StatInDto; 4 | import ru.practicum.ewm.dto.stats.StatOutDto; 5 | import ru.practicum.ewm.model.Stat; 6 | 7 | import java.util.List; 8 | 9 | public interface StatsService { 10 | void saveHit(StatInDto statInDto); 11 | 12 | List getHits(String start, String end, List uris, Boolean unique); 13 | 14 | List getAllHits(Integer from, Integer size); 15 | } 16 | -------------------------------------------------------------------------------- /stats/src/main/java/ru/practicum/ewm/service/StatsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import ru.practicum.ewm.Constants; 10 | import ru.practicum.ewm.dto.stats.StatInDto; 11 | import ru.practicum.ewm.dto.stats.StatOutDto; 12 | import ru.practicum.ewm.mapper.StatMapper; 13 | import ru.practicum.ewm.model.Stat; 14 | import ru.practicum.ewm.repository.StatsRepository; 15 | 16 | import javax.validation.Valid; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | 20 | @Service 21 | @RequiredArgsConstructor 22 | public class StatsServiceImpl implements StatsService { 23 | private final StatsRepository statsRepository; 24 | 25 | @Override 26 | @Transactional 27 | public void saveHit(@Valid @RequestBody StatInDto statInDto) { 28 | Stat stats = StatMapper.dtoToStat(statInDto); 29 | statsRepository.save(stats); 30 | } 31 | 32 | @Override 33 | public List getHits(String start, String end, List uris, Boolean unique) { 34 | List stats = List.of(); 35 | if (uris.size() == 0) { 36 | if (unique) { 37 | stats = statsRepository.countByTimestampUniqueIp( 38 | LocalDateTime.parse(start, Constants.DATE_TIME_SPACE), 39 | LocalDateTime.parse(end, Constants.DATE_TIME_SPACE)); 40 | } else { 41 | stats = statsRepository.countByTimestamp( 42 | LocalDateTime.parse(start, Constants.DATE_TIME_SPACE), 43 | LocalDateTime.parse(end, Constants.DATE_TIME_SPACE)); 44 | } 45 | } else { 46 | if (unique) { 47 | stats = statsRepository.countByTimestampAndListUniqueIp( 48 | LocalDateTime.parse(start, Constants.DATE_TIME_SPACE), 49 | LocalDateTime.parse(end, Constants.DATE_TIME_SPACE), 50 | uris); 51 | } else { 52 | stats = statsRepository.countByTimestampAndList( 53 | LocalDateTime.parse(start, Constants.DATE_TIME_SPACE), 54 | LocalDateTime.parse(end, Constants.DATE_TIME_SPACE), 55 | uris); 56 | } 57 | } 58 | return stats; 59 | } 60 | 61 | @Override 62 | public List getAllHits(Integer from, Integer size) { 63 | Pageable pageable = PageRequest.of(from / size, size); 64 | return statsRepository.findAll(pageable).toList(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /stats/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 2 | # port = 9090 look at StatsAPI.java 3 | spring.sql.init.mode=always 4 | spring.jpa.hibernate.ddl-auto=none 5 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect 6 | spring.jpa.properties.hibernate.format_sql=true 7 | # docker 8 | #--- 9 | spring.datasource.driver-class-name=org.postgresql.Driver 10 | spring.datasource.url=${SPRING_DATASOURCE_URL} 11 | spring.datasource.username=${POSTGRES_USER} 12 | spring.datasource.password=${POSTGRES_PASSWORD} 13 | #--- 14 | spring.config.activate.on-profile=ci,test 15 | spring.datasource.driverClassName=org.h2.Driver 16 | spring.datasource.url=jdbc:h2:mem:stats 17 | spring.datasource.username=test 18 | spring.datasource.password=test 19 | # sql show debug 20 | #spring.jpa.show-sql=true 21 | #logging.level.ru.practicum=DEBUG -------------------------------------------------------------------------------- /stats/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS stats cascade; 2 | 3 | CREATE TABLE IF NOT EXISTS stats 4 | ( 5 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 6 | app VARCHAR(30) NOT NULL, 7 | uri VARCHAR(250) NOT NULL, 8 | ip VARCHAR(50) NOT NULL, 9 | timestamp TIMESTAMP WITHOUT TIME ZONE 10 | ); -------------------------------------------------------------------------------- /stats/src/test/java/ru/practicum/ewm/StatsAPITest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class StatsAPITest { 8 | @Test 9 | void contextLoads() { 10 | } 11 | } -------------------------------------------------------------------------------- /stats/src/test/java/ru/practicum/ewm/dto/stats/StatInDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.stats; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import ru.practicum.ewm.Constants; 8 | 9 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 10 | 11 | @JsonTest 12 | class StatInDtoTest { 13 | @Autowired 14 | private JacksonTester json; 15 | 16 | @Test 17 | void testSerialize() throws Exception { 18 | var dto = new StatInDto( 19 | "App name", 20 | "https://some.uri.com/events/1", 21 | "10.0.65.1", 22 | "2022-10-19 11:10:00" 23 | ); 24 | 25 | var result = json.write(dto); 26 | assertThat(result).hasJsonPath("$.app"); 27 | assertThat(result).hasJsonPath("$.uri"); 28 | assertThat(result).hasJsonPath("$.ip"); 29 | assertThat(result).hasJsonPath("$.timestamp"); 30 | 31 | assertThat(result).extractingJsonPathStringValue("$.app").isEqualTo(dto.getApp()); 32 | assertThat(result).extractingJsonPathStringValue("$.uri").isEqualTo(dto.getUri()); 33 | assertThat(result).extractingJsonPathStringValue("$.ip").isEqualTo(dto.getIp()); 34 | assertThat(result).extractingJsonPathStringValue("$.timestamp") 35 | .startsWith(dto.getTimestamp().format(Constants.DATE_TIME_SPACE)); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /stats/src/test/java/ru/practicum/ewm/dto/stats/StatOutDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.dto.stats; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | 8 | import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; 9 | 10 | @JsonTest 11 | class StatOutDtoTest { 12 | @Autowired 13 | private JacksonTester json; 14 | 15 | @Test 16 | void testSerialize() throws Exception { 17 | var dto = new StatOutDto( 18 | "App name", 19 | "https://some.uri.com/events/1", 20 | 5L 21 | ); 22 | 23 | var result = json.write(dto); 24 | assertThat(result).hasJsonPath("$.app"); 25 | assertThat(result).hasJsonPath("$.uri"); 26 | assertThat(result).hasJsonPath("$.hits"); 27 | 28 | assertThat(result).extractingJsonPathStringValue("$.app").isEqualTo(dto.getApp()); 29 | assertThat(result).extractingJsonPathStringValue("$.uri").isEqualTo(dto.getUri()); 30 | assertThat(result).extractingJsonPathNumberValue("$.hits").isEqualTo(dto.getHits().intValue()); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /stats/src/test/java/ru/practicum/ewm/mapper/StatMapperTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.ewm.mapper; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import ru.practicum.ewm.Constants; 6 | import ru.practicum.ewm.dto.stats.StatInDto; 7 | import ru.practicum.ewm.model.Stat; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | class StatMapperTest { 14 | private StatInDto statInDto; 15 | private Stat expected; 16 | 17 | @BeforeEach 18 | void setUp() { 19 | statInDto = new StatInDto( 20 | "App name", 21 | "https://some.uri.com/events/1", 22 | "10.0.65.1", 23 | "2022-10-19 11:10:00" 24 | ); 25 | expected = new Stat( 26 | null, 27 | "App name", 28 | "https://some.uri.com/events/1", 29 | "10.0.65.1", 30 | LocalDateTime.parse("2022-10-19 11:10:00", Constants.DATE_TIME_SPACE) 31 | ); 32 | } 33 | 34 | @Test 35 | void dtoToStat() { 36 | Stat result = StatMapper.dtoToStat(statInDto); 37 | 38 | assertThat(expected.getId()).isEqualTo(result.getId()); 39 | assertThat(expected.getApp()).isEqualTo(result.getApp()); 40 | assertThat(expected.getUri()).isEqualTo(result.getUri()); 41 | assertThat(expected.getIp()).isEqualTo(result.getIp()); 42 | assertThat(expected.getTimestamp()).isEqualTo(result.getTimestamp()); 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /uml/db-ewm-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bannovdaniil/java-explore-with-me/569f6d9c23d1c6ac295970cb85644d513125288e/uml/db-ewm-image.png -------------------------------------------------------------------------------- /uml/db-ewm.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | !theme plain 4 | top to bottom direction 5 | skinparam linetype ortho 6 | 7 | class CATEGORIES { 8 | NAME: character varying(200) 9 | ID: bigint 10 | } 11 | class COMPILATIONS { 12 | TITLE: character varying(120) 13 | PINNED: boolean 14 | ID: bigint 15 | } 16 | class COMPILATIONS_EVENTS { 17 | EVENT_ID: bigint 18 | COMPILATION_ID: bigint 19 | ID: bigint 20 | } 21 | class EVENTS { 22 | ANNOTATION: character large object 23 | CATEGORY_ID: bigint 24 | CONFIRMED_REQUESTS: integer 25 | CREATED_ON: timestamp 26 | DESCRIPTION: character large object 27 | EVENT_DATE: timestamp 28 | INITIATOR_ID: bigint 29 | LOCATION_ID: bigint 30 | PAID: boolean 31 | PARTICIPANT_LIMIT: integer 32 | PUBLISHED_ON: timestamp 33 | REQUEST_MODERATION: boolean 34 | STATE: character varying(15) 35 | TITLE: character varying(120) 36 | VIEWS: bigint 37 | RATE: integer 38 | ID: bigint 39 | } 40 | class LIKES { 41 | USER_ID: bigint 42 | EVENT_ID: bigint 43 | TYPE: character varying(7) 44 | ID: bigint 45 | } 46 | class LOCATIONS { 47 | LAT: double precision 48 | LON: double precision 49 | ID: bigint 50 | } 51 | class REQUESTS { 52 | REQUESTER_ID: bigint 53 | CREATED: timestamp 54 | STATUS: character varying(15) 55 | EVENT_ID: bigint 56 | ID: bigint 57 | } 58 | class STATS { 59 | APP: character varying(30) 60 | URI: character varying(250) 61 | IP: character varying(50) 62 | TIMESTAMP: timestamp 63 | ID: bigint 64 | } 65 | class USERS { 66 | NAME: character varying(250) 67 | EMAIL: character varying(100) 68 | RATE: real 69 | ID: bigint 70 | } 71 | 72 | COMPILATIONS_EVENTS -[#595959,plain]-^ COMPILATIONS : "COMPILATION_ID:ID" 73 | COMPILATIONS_EVENTS -[#595959,plain]-^ EVENTS : "EVENT_ID:ID" 74 | EVENTS -[#595959,plain]-^ CATEGORIES : "CATEGORY_ID:ID" 75 | EVENTS -[#595959,plain]-^ LOCATIONS : "LOCATION_ID:ID" 76 | EVENTS -[#595959,plain]-^ USERS : "INITIATOR_ID:ID" 77 | LIKES -[#595959,plain]-^ EVENTS : "EVENT_ID:ID" 78 | LIKES -[#595959,plain]-^ USERS : "USER_ID:ID" 79 | REQUESTS -[#595959,plain]-^ EVENTS : "EVENT_ID:ID" 80 | REQUESTS -[#595959,plain]-^ USERS : "REQUESTER_ID:ID" 81 | @enduml 82 | -------------------------------------------------------------------------------- /uml/project.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | scale 1000 width 3 | left to right direction 4 | skinparam actorStyle awesome 5 | skinparam storageStyle rectangle 6 | skinparam cloudFontSize 24 7 | 8 | actor User #skyblue 9 | 10 | cloud Gateway #line:blue { 11 | agent "Login User API" as Login 12 | } 13 | 14 | User -> Login #line:blue; 15 | 16 | cloud "Main service API port: 8080" as Main #line:green { 17 | 18 | actor "Login User" as loginUser #lightgreen 19 | actor Admin #pink 20 | 21 | node Controller as MainController { 22 | agent "Public" as PublicArea #skyblue 23 | agent "Private" as PrivateArea #lightgreen 24 | agent "Admin" as AdminArea #pink 25 | } 26 | 27 | User <--> PublicArea #line:blue; 28 | 29 | loginUser <--> PublicArea #line:green; 30 | loginUser <--> PrivateArea #line:green; 31 | 32 | Admin <--> PublicArea #line:red; 33 | Admin <--> PrivateArea #line:red; 34 | Admin <--> AdminArea #line:red; 35 | 36 | node Service { 37 | agent "Public" as SrvPublicArea 38 | agent "Private" as SrvPrivateArea 39 | agent "Admin" as SrvAdminArea 40 | } 41 | 42 | PublicArea <--> SrvPublicArea 43 | PrivateArea <--> SrvPrivateArea 44 | AdminArea <--> SrvAdminArea 45 | 46 | node "Jpa Repository" as repoArea { 47 | card "Users" as RepoUserArea 48 | card "Events" as RepoEventArea 49 | card "Requests" as RepoRequestArea 50 | card "Events" as RepoEventArea 51 | card "Categories" as RepoCategoryArea 52 | card "Locations" as RepoLocationArea 53 | card "Compilations" as RepoCompilationsArea 54 | } 55 | 56 | SrvPublicArea <--> repoArea 57 | SrvPrivateArea <--> repoArea 58 | SrvAdminArea <--> repoArea 59 | 60 | database "Data Base" { 61 | agent "Main DB" as DB 62 | } 63 | 64 | repoArea <--> DB 65 | 66 | } 67 | 68 | cloud "Stat service API : 9090" as Stats #line:red; { 69 | node Controller as StatsController { 70 | agent "Get Stats" as GetStatsArea 71 | agent "Save Stats" as SaveStatsArea 72 | } 73 | 74 | node Service as StatsService { 75 | agent "Service" as SrvViewArea 76 | } 77 | 78 | GetStatsArea <--> SrvViewArea 79 | SaveStatsArea --> SrvViewArea 80 | 81 | node Repository as StatsRepository { 82 | agent "Repository" as RepoViewArea 83 | } 84 | 85 | SrvViewArea <--> RepoViewArea 86 | 87 | database "Data Base" as StatsDB { 88 | agent "Stats DB" as nameStatsDB 89 | 90 | } 91 | RepoViewArea <--> nameStatsDB 92 | } 93 | 94 | Admin <--> GetStatsArea #line:red;line.bold; 95 | 96 | SaveStatsArea <-- SrvPublicArea 97 | 98 | Login --> loginUser 99 | Login --> Admin 100 | 101 | @enduml 102 | --------------------------------------------------------------------------------