├── main-service ├── Dockerfile └── src │ ├── main │ ├── resources │ │ ├── static │ │ │ ├── images │ │ │ │ ├── cost.png │ │ │ │ ├── location.png │ │ │ │ ├── people.png │ │ │ │ ├── date-time.png │ │ │ │ ├── logo-text.png │ │ │ │ ├── description.png │ │ │ │ ├── logo-compass.png │ │ │ │ ├── search-icon.png │ │ │ │ ├── x-close-btn.png │ │ │ │ ├── google-play-logo.svg │ │ │ │ └── app-store-logo.svg │ │ │ ├── js │ │ │ │ └── header-script.js │ │ │ └── css │ │ │ │ ├── user-settings.css │ │ │ │ ├── sign-up-in-styles.css │ │ │ │ └── add-event.css │ │ ├── import.sql │ │ ├── templates │ │ │ ├── sign-in.html │ │ │ ├── sign-up.html │ │ │ ├── fragments │ │ │ │ ├── header.html │ │ │ │ └── footer.html │ │ │ └── profile.html │ │ ├── application.properties │ │ └── schema.sql │ └── java │ │ └── ru │ │ └── practicum │ │ ├── event │ │ ├── model │ │ │ ├── enums │ │ │ │ ├── EventSort.java │ │ │ │ ├── EventStatus.java │ │ │ │ ├── EventState.java │ │ │ │ └── StateAction.java │ │ │ ├── Location.java │ │ │ └── Event.java │ │ ├── repository │ │ │ ├── LocationRepository.java │ │ │ ├── EventRepository.java │ │ │ └── EventSpecRepository.java │ │ ├── dto │ │ │ ├── EventRequestStatusUpdateResultDto.java │ │ │ ├── EventRequestStatusUpdateRequest.java │ │ │ ├── EventShortDto.java │ │ │ ├── EventUpdateDto.java │ │ │ ├── NewEventDto.java │ │ │ └── EventFullDto.java │ │ ├── service │ │ │ └── EventService.java │ │ ├── mapper │ │ │ └── EventMapper.java │ │ └── controller │ │ │ ├── PublicEventController.java │ │ │ └── AdminEventController.java │ │ ├── comment │ │ ├── model │ │ │ ├── CommentStatus.java │ │ │ └── Comment.java │ │ ├── dto │ │ │ ├── NewCommentDto.java │ │ │ ├── CommentStatusUpdateRequest.java │ │ │ └── CommentDto.java │ │ ├── mapper │ │ │ └── CommentMapper.java │ │ ├── repository │ │ │ ├── CommentRepository.java │ │ │ └── CommentSpecRepository.java │ │ ├── service │ │ │ └── CommentService.java │ │ └── controller │ │ │ └── AdminCommentController.java │ │ ├── request │ │ ├── model │ │ │ ├── ParticipationStatus.java │ │ │ └── ParticipationRequest.java │ │ ├── dto │ │ │ └── ParticipationRequestDto.java │ │ ├── mapper │ │ │ └── RequestMapper.java │ │ ├── repository │ │ │ └── RequestRepository.java │ │ ├── service │ │ │ └── RequestService.java │ │ └── controller │ │ │ └── PrivateRequestController.java │ │ ├── exception │ │ ├── ObjectNotFoundException.java │ │ ├── RequestConflictException.java │ │ ├── ConditionsNotMetException.java │ │ ├── IncorrectRequestException.java │ │ ├── SQLConstraintViolationException.java │ │ ├── UserAuthException.java │ │ ├── ApiError.java │ │ └── MainServiceExceptionHandler.java │ │ ├── MainService.java │ │ ├── category │ │ ├── repository │ │ │ └── CategoryRepository.java │ │ ├── dto │ │ │ ├── NewCategoryDto.java │ │ │ └── CategoryDto.java │ │ ├── service │ │ │ ├── CategoryService.java │ │ │ └── CategoryServiceImpl.java │ │ ├── mapper │ │ │ └── CategoryMapper.java │ │ ├── model │ │ │ └── Category.java │ │ └── controller │ │ │ ├── PublicCategoryController.java │ │ │ └── AdminCategoryController.java │ │ ├── user │ │ ├── dto │ │ │ ├── UserShortDto.java │ │ │ ├── UserLoginDto.java │ │ │ ├── UserDto.java │ │ │ └── NewUserDto.java │ │ ├── repository │ │ │ ├── RoleRepository.java │ │ │ └── UserRepository.java │ │ ├── service │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ │ ├── mapper │ │ │ └── UserMapper.java │ │ ├── model │ │ │ ├── Role.java │ │ │ └── User.java │ │ └── controller │ │ │ └── AdminUserController.java │ │ ├── compilation │ │ ├── dto │ │ │ ├── UpdateCompilationRequest.java │ │ │ ├── NewCompilationDto.java │ │ │ └── CompilationDto.java │ │ ├── repository │ │ │ └── CompilationRepository.java │ │ ├── service │ │ │ ├── CompilationService.java │ │ │ └── CompilationServiceImpl.java │ │ ├── mapper │ │ │ └── CompilationMapper.java │ │ ├── model │ │ │ └── Compilation.java │ │ └── controller │ │ │ ├── PublicCompilationController.java │ │ │ └── AdminCompilationController.java │ │ ├── web │ │ ├── IndexController.java │ │ ├── UserProfileController.java │ │ ├── UserEventController.java │ │ └── AuthController.java │ │ ├── security │ │ ├── WebSecurityConfig.java │ │ └── AuthenticationProvider.java │ │ └── aspect │ │ └── WebControllerLogger.java │ └── test │ └── java │ └── ru │ └── practicum │ ├── comment │ ├── dto │ │ ├── NewCommentDtoTest.java │ │ ├── CommentStatusUpdateRequestTest.java │ │ └── CommentDtoTest.java │ └── mapper │ │ └── CommentMapperTest.java │ ├── category │ ├── mapper │ │ └── CategoryMapperTest.java │ ├── dto │ │ ├── NewCategoryDtoTest.java │ │ └── CategoryDtoTest.java │ └── controller │ │ ├── PublicCategoryControllerTest.java │ │ └── AdminCategoryControllerTest.java │ └── web │ └── AuthControllerTest.java ├── stats-service ├── server │ ├── Dockerfile │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── ru │ │ │ │ └── practicum │ │ │ │ ├── repository │ │ │ │ ├── AppRepository.java │ │ │ │ └── RequestRepository.java │ │ │ │ ├── StatsServiceApp.java │ │ │ │ ├── model │ │ │ │ ├── App.java │ │ │ │ └── Request.java │ │ │ │ ├── service │ │ │ │ ├── StatsService.java │ │ │ │ └── StatsServiceImpl.java │ │ │ │ ├── mapper │ │ │ │ └── RequestMapper.java │ │ │ │ └── controller │ │ │ │ └── StatsController.java │ │ │ ├── resources │ │ │ ├── schema.sql │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── ru │ │ │ └── practicum │ │ │ ├── dto │ │ │ ├── RequestOutputDtoTest.java │ │ │ └── RequestDtoTest.java │ │ │ ├── controller │ │ │ └── StatsControllerTest.java │ │ │ └── StatsServiceTest.java │ └── pom.xml ├── dto │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── ru │ │ │ └── practicum │ │ │ ├── RequestOutputDto.java │ │ │ └── RequestDto.java │ └── pom.xml └── client │ ├── pom.xml │ └── src │ └── main │ └── java │ └── ru │ └── practicum │ └── StatsClient.java ├── lombok.config ├── .github └── workflows │ └── api-tests.yml ├── suppressions.xml ├── .gitignore └── docker-compose.yml /main-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:11 2 | COPY target/*.jar server.jar 3 | ENTRYPOINT ["java","-jar","/server.jar"] 4 | -------------------------------------------------------------------------------- /stats-service/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:11 2 | COPY target/*.jar server.jar 3 | ENTRYPOINT ["java","-jar","/server.jar"] 4 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/cost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/cost.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/location.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/people.png -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.anyconstructor.addconstructorproperties = false 3 | lombok.addLombokGeneratedAnnotation = true 4 | lombok.addSuppressWarnings = false -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/date-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/date-time.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/logo-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/logo-text.png -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/enums/EventSort.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.model.enums; 2 | 3 | public enum EventSort { 4 | EVENT_DATE, 5 | VIEWS 6 | } 7 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/description.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/description.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/logo-compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/logo-compass.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/search-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/search-icon.png -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/x-close-btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cptntotoro/java-explore-with-me/HEAD/main-service/src/main/resources/static/images/x-close-btn.png -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/enums/EventStatus.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.model.enums; 2 | 3 | public enum EventStatus { 4 | CONFIRMED, 5 | REJECTED 6 | } 7 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/model/CommentStatus.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.model; 2 | 3 | public enum CommentStatus { 4 | PENDING, 5 | PUBLISHED, 6 | DELETED 7 | } 8 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/enums/EventState.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.model.enums; 2 | 3 | public enum EventState { 4 | PENDING, 5 | PUBLISHED, 6 | CANCELED 7 | } 8 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/model/ParticipationStatus.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.model; 2 | 3 | public enum ParticipationStatus { 4 | CONFIRMED, 5 | REJECTED, 6 | CANCELED, 7 | PENDING 8 | } 9 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/enums/StateAction.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.model.enums; 2 | 3 | public enum StateAction { 4 | SEND_TO_REVIEW, 5 | CANCEL_REVIEW, 6 | PUBLISH_EVENT, 7 | REJECT_EVENT 8 | } 9 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/ObjectNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | public class ObjectNotFoundException extends RuntimeException { 4 | public ObjectNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/RequestConflictException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | public class RequestConflictException extends RuntimeException { 4 | public RequestConflictException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/ConditionsNotMetException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | public class ConditionsNotMetException extends RuntimeException { 4 | public ConditionsNotMetException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/IncorrectRequestException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | public class IncorrectRequestException extends RuntimeException { 4 | public IncorrectRequestException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/SQLConstraintViolationException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | public class SQLConstraintViolationException extends RuntimeException { 4 | public SQLConstraintViolationException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/UserAuthException.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | public class UserAuthException extends AuthenticationException { 6 | 7 | public UserAuthException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/repository/AppRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import ru.practicum.model.App; 5 | 6 | import java.util.Optional; 7 | 8 | public interface AppRepository extends JpaRepository { 9 | Optional findByName(String app); 10 | } 11 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/MainService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MainService { 8 | public static void main(String[] args) { 9 | SpringApplication.run(MainService.class, args); 10 | } 11 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/repository/LocationRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import ru.practicum.event.model.Location; 6 | 7 | @Repository 8 | public interface LocationRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import ru.practicum.category.model.Category; 6 | 7 | @Repository 8 | public interface CategoryRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/StatsServiceApp.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class StatsServiceApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(StatsServiceApp.class); 11 | } 12 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/dto/UserShortDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotNull; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserShortDto { 13 | private Long id; 14 | 15 | @NotNull 16 | private String name; 17 | } 18 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/repository/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import ru.practicum.user.model.Role; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface RoleRepository extends JpaRepository { 11 | 12 | Optional getByName(String nameW); 13 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/dto/NewCommentDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotBlank; 8 | import javax.validation.constraints.Size; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class NewCommentDto { 14 | 15 | @NotBlank 16 | @Size(min = 5, max = 5000) 17 | private String text; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/EventRequestStatusUpdateResultDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import ru.practicum.request.dto.ParticipationRequestDto; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class EventRequestStatusUpdateResultDto { 14 | List confirmedRequests; 15 | List rejectedRequests; 16 | } 17 | -------------------------------------------------------------------------------- /stats-service/server/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS apps, requests CASCADE; 2 | 3 | CREATE TABLE IF NOT EXISTS apps ( 4 | id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, 5 | name varchar(512) NOT NULL 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS requests ( 9 | id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, 10 | app_id BIGINT REFERENCES apps(id) ON DELETE CASCADE, 11 | uri varchar(512) NOT NULL, 12 | ip varchar(15) NOT NULL, 13 | time_stamp TIMESTAMP WITHOUT TIME ZONE 14 | ); -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/dto/UserLoginDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotNull; 8 | import javax.validation.constraints.Size; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class UserLoginDto { 14 | 15 | @NotNull 16 | @Size(min = 5) 17 | private String username; 18 | 19 | @NotNull 20 | @Size(min = 5) 21 | private String password; 22 | } -------------------------------------------------------------------------------- /stats-service/dto/src/main/java/ru/practicum/RequestOutputDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotBlank; 8 | import java.io.Serializable; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class RequestOutputDto implements Serializable { 14 | 15 | @NotBlank 16 | private String app; 17 | 18 | @NotBlank 19 | private String uri; 20 | 21 | @NotBlank 22 | private Long hits; 23 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/ApiError.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Getter 10 | @AllArgsConstructor 11 | public class ApiError { 12 | private String status; 13 | private String reason; 14 | private String message; 15 | 16 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 17 | private LocalDateTime timestamp; 18 | } 19 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/dto/NewCategoryDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.experimental.SuperBuilder; 7 | import org.hibernate.validator.constraints.Length; 8 | 9 | import javax.validation.constraints.NotBlank; 10 | 11 | @Data 12 | @SuperBuilder 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class NewCategoryDto { 16 | 17 | @NotBlank 18 | @Length(max = 50) 19 | private String name; 20 | } 21 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.service; 2 | 3 | import ru.practicum.category.dto.NewCategoryDto; 4 | import ru.practicum.category.model.Category; 5 | 6 | import java.util.List; 7 | 8 | public interface CategoryService { 9 | List getAll(Integer from, Integer size); 10 | 11 | Category get(Long categoryId); 12 | 13 | Category add(NewCategoryDto body); 14 | 15 | void delete(Long catId); 16 | 17 | Category update(Long categoryId, NewCategoryDto newCategoryDto); 18 | } 19 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import java.util.Set; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class UpdateCompilationRequest { 14 | 15 | private Set events; 16 | 17 | private Boolean pinned; 18 | 19 | @Length(min = 1, max = 50) 20 | private String title; 21 | } 22 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.repository; 2 | 3 | import org.springframework.data.domain.Pageable; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | import ru.practicum.compilation.model.Compilation; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface CompilationRepository extends JpaRepository { 12 | 13 | List findAllByPinned(Boolean pinned, Pageable pageable); 14 | } 15 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/dto/UserDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.Email; 8 | import javax.validation.constraints.NotNull; 9 | import javax.validation.constraints.Positive; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class UserDto { 15 | @Positive 16 | private Long id; 17 | 18 | @NotNull 19 | @Email 20 | private String email; 21 | 22 | @NotNull 23 | private String name; 24 | } 25 | -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/model/App.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.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 = "apps") 12 | @Getter 13 | @Setter 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class App { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Integer id; 21 | 22 | private String name; 23 | 24 | public App(String name) { 25 | this.name = name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/service/StatsService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.service; 2 | 3 | import ru.practicum.RequestDto; 4 | import ru.practicum.RequestOutputDto; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | public interface StatsService { 10 | void addRequest(RequestDto requestDto); 11 | 12 | List getRequestsWithViews(LocalDateTime start, LocalDateTime end, List uris, Boolean unique); 13 | 14 | List getRequestsWithViewsByIp(LocalDateTime startDT, LocalDateTime endDT, List uris, Boolean unique, String ip); 15 | } 16 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/dto/NewCompilationDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | import java.util.Set; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class NewCompilationDto { 15 | 16 | private Set events; 17 | 18 | private Boolean pinned = false; 19 | 20 | @NotBlank 21 | @Length(min = 1, max = 50) 22 | private String title; 23 | } 24 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.validator.constraints.Length; 7 | import ru.practicum.event.dto.EventShortDto; 8 | 9 | import javax.validation.constraints.NotBlank; 10 | import java.util.Set; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class CompilationDto { 16 | 17 | private Integer id; 18 | 19 | private Set events; 20 | 21 | private Boolean pinned; 22 | 23 | @NotBlank 24 | @Length(min = 1, max = 50) 25 | private String title; 26 | } 27 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/web/IndexController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.web; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import ru.practicum.user.model.User; 9 | 10 | @Controller 11 | @RequestMapping 12 | @RequiredArgsConstructor 13 | public class IndexController { 14 | 15 | @GetMapping("/") 16 | public String getIndex(Model model) { 17 | 18 | User user = new User(); 19 | model.addAttribute("user", user); 20 | 21 | return "index"; 22 | } 23 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | import ru.practicum.user.model.User; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | @Repository 13 | public interface UserRepository extends JpaRepository { 14 | 15 | Page findAllByIdIn(List ids, Pageable pageable); 16 | 17 | Optional findByEmail(String email); 18 | 19 | Optional findByUsername(String name); 20 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/dto/ParticipationRequestDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import ru.practicum.request.model.ParticipationStatus; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class ParticipationRequestDto { 15 | private Long id; 16 | private Long event; 17 | private Long requester; 18 | private ParticipationStatus status; 19 | 20 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 21 | private LocalDateTime created; 22 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/mapper/RequestMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.factory.Mappers; 7 | import ru.practicum.RequestDto; 8 | import ru.practicum.model.Request; 9 | 10 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) 11 | public interface RequestMapper { 12 | RequestMapper INSTANCE = Mappers.getMapper(RequestMapper.class); 13 | 14 | @Mapping(source = "app.name", target = "app") 15 | RequestDto requestToRequestDto(Request request); 16 | 17 | @Mapping(source = "app", target = "app.name") 18 | Request requestDtoToRequest(RequestDto requestDto); 19 | } 20 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/web/UserProfileController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.web; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import ru.practicum.user.dto.NewUserDto; 10 | 11 | @Controller 12 | @RequestMapping("/users/profile") 13 | @RequiredArgsConstructor 14 | @Slf4j 15 | public class UserProfileController { 16 | 17 | @GetMapping 18 | public String getUserProfile(Model model) { 19 | model.addAttribute("user", new NewUserDto()); 20 | return "profile"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/dto/CommentStatusUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import ru.practicum.comment.model.CommentStatus; 7 | 8 | import javax.persistence.EnumType; 9 | import javax.persistence.Enumerated; 10 | import javax.validation.constraints.NotEmpty; 11 | import javax.validation.constraints.NotNull; 12 | import java.util.List; 13 | 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class CommentStatusUpdateRequest { 18 | 19 | @NotNull 20 | @NotEmpty 21 | private List commentIds; 22 | 23 | @NotNull 24 | @Enumerated(EnumType.STRING) 25 | private CommentStatus status; 26 | } 27 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/service/CompilationService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | import ru.practicum.compilation.dto.NewCompilationDto; 5 | import ru.practicum.compilation.dto.UpdateCompilationRequest; 6 | import ru.practicum.compilation.model.Compilation; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | public interface CompilationService { 12 | Compilation add(NewCompilationDto compilationDto); 13 | 14 | void delete(Long compilationId); 15 | 16 | Compilation get(Long compId); 17 | 18 | List getAll(Boolean pinned, Integer from, Integer size); 19 | 20 | Compilation update(Long compId, UpdateCompilationRequest compilationRequest); 21 | } 22 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/EventRequestStatusUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import ru.practicum.event.model.enums.EventStatus; 7 | 8 | import javax.persistence.EnumType; 9 | import javax.persistence.Enumerated; 10 | import javax.validation.constraints.NotEmpty; 11 | import javax.validation.constraints.NotNull; 12 | import java.util.List; 13 | 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class EventRequestStatusUpdateRequest { 18 | 19 | @NotNull 20 | @NotEmpty 21 | private List requestIds; 22 | 23 | @NotNull 24 | @Enumerated(EnumType.STRING) 25 | private EventStatus status; 26 | } 27 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/Location.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.GenerationType; 12 | import javax.persistence.Id; 13 | import javax.persistence.Table; 14 | 15 | 16 | @Entity 17 | @Table(name = "locations") 18 | @Getter 19 | @Setter 20 | @ToString 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class Location { 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.IDENTITY) 27 | private Integer id; 28 | private String description; 29 | private Double lat; 30 | private Double lon; 31 | } 32 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/service/UserService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.service; 2 | 3 | import org.springframework.security.core.userdetails.UserDetailsService; 4 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 5 | import ru.practicum.user.dto.NewUserDto; 6 | import ru.practicum.user.model.User; 7 | 8 | import java.util.List; 9 | 10 | public interface UserService extends UserDetailsService { 11 | List getAll(); 12 | 13 | List getByIds(List ids, Integer from, Integer size); 14 | 15 | User addAdminUser(User user); 16 | 17 | void delete(Long userId); 18 | 19 | List getUsersWithIdBiggerThan(Long idMin); 20 | 21 | User add(NewUserDto user); 22 | 23 | User loadUserByUsername(String username) throws UsernameNotFoundException; 24 | } 25 | -------------------------------------------------------------------------------- /main-service/src/main/resources/import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO roles(name) VALUES ('ROLE_USER'); 2 | INSERT INTO roles(name) VALUES ('ROLE_ADMIN'); 3 | 4 | INSERT INTO categories(name) VALUES ('Выставки'); 5 | INSERT INTO categories(name) VALUES ('Концерты'); 6 | INSERT INTO categories(name) VALUES ('Театр'); 7 | INSERT INTO categories(name) VALUES ('Обучение'); 8 | INSERT INTO categories(name) VALUES ('Кино'); 9 | INSERT INTO categories(name) VALUES ('Фестивали'); 10 | INSERT INTO categories(name) VALUES ('Экскурсии'); 11 | INSERT INTO categories(name) VALUES ('Стендапы'); 12 | INSERT INTO categories(name) VALUES ('Квесты'); 13 | 14 | INSERT INTO users(name, username, password, email) VALUES ('admin', 'admin', 'admin', 'admin@admin.ru'); 15 | 16 | INSERT INTO user_roles(user_id, role_id) VALUES (1, 1); 17 | INSERT INTO user_roles(user_id, role_id) VALUES (1, 2); -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/dto/CategoryDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | import lombok.experimental.SuperBuilder; 9 | import org.hibernate.validator.constraints.Length; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | 13 | @Data 14 | @SuperBuilder 15 | @EqualsAndHashCode(callSuper = true) 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class CategoryDto extends NewCategoryDto { 19 | 20 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) 21 | private Long id; 22 | 23 | public CategoryDto(@NotBlank @Length(max = 50) String name, Long id) { 24 | super(name); 25 | this.id = id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.factory.Mappers; 7 | import ru.practicum.user.dto.NewUserDto; 8 | import ru.practicum.user.dto.UserDto; 9 | import ru.practicum.user.model.User; 10 | 11 | import java.util.List; 12 | 13 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) 14 | public interface UserMapper { 15 | UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); 16 | 17 | UserDto userToUserDto(User user); 18 | 19 | List listUserToListUserDto(List user); 20 | 21 | @Mapping(target = "id", ignore = true) 22 | @Mapping(target = "roles", ignore = true) 23 | User newUserRequestDtoToUser(NewUserDto newUserDto); 24 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/mapper/RequestMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.factory.Mappers; 6 | import ru.practicum.request.dto.ParticipationRequestDto; 7 | import ru.practicum.request.model.ParticipationRequest; 8 | 9 | import java.util.List; 10 | 11 | @Mapper(componentModel = "spring") 12 | public interface RequestMapper { 13 | RequestMapper INSTANCE = Mappers.getMapper(RequestMapper.class); 14 | 15 | @Mapping(target = "event", source = "event.id") 16 | @Mapping(target = "requester", source = "requester.id") 17 | ParticipationRequestDto requestToRequestDto(ParticipationRequest participationRequest); 18 | 19 | List listRequestToListRequestDto(List participationRequest); 20 | } 21 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/repository/RequestRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import ru.practicum.request.model.ParticipationRequest; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface RequestRepository extends JpaRepository { 10 | 11 | Boolean existsByRequesterIdAndEventId(Long userId, Long eventId); 12 | 13 | List findAllByIdInAndAndEventId(Iterable ids, Long eventId); 14 | 15 | List findAllByRequesterId(Long userId); 16 | 17 | List findAllByEventIdAndEventInitiatorId(Long eventId, Long userId); 18 | 19 | Optional findByIdAndRequesterId(Long requestId, Long userId); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.factory.Mappers; 7 | import ru.practicum.category.dto.CategoryDto; 8 | import ru.practicum.category.dto.NewCategoryDto; 9 | import ru.practicum.category.model.Category; 10 | 11 | import java.util.List; 12 | 13 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) 14 | public interface CategoryMapper { 15 | CategoryMapper INSTANCE = Mappers.getMapper(CategoryMapper.class); 16 | 17 | CategoryDto categoryToCategoryDto(Category category); 18 | 19 | List listCategoryToListCategoryDto(List category); 20 | 21 | @Mapping(target = "id", ignore = true) 22 | Category newCategoryDtoToCategory(NewCategoryDto newCategoryDto); 23 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/model/Category.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.persistence.Column; 10 | import javax.persistence.Entity; 11 | import javax.persistence.GeneratedValue; 12 | import javax.persistence.GenerationType; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | 16 | @Entity 17 | @Table(name = "categories") 18 | @Getter 19 | @Setter 20 | @ToString 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class Category { 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.IDENTITY) 27 | private Long id; 28 | 29 | @Column(name = "name", unique = true) 30 | private String name; 31 | 32 | public Category(Long id) { 33 | this.id = id; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/comment/dto/NewCommentDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 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.Assertions.assertThat; 9 | 10 | @JsonTest 11 | public class NewCommentDtoTest { 12 | 13 | @Autowired 14 | private JacksonTester jacksonTester; 15 | 16 | @Test 17 | void testDeserialize() throws Exception { 18 | String jsonValue = "{\"text\": \"Do they serve lactose-free latte there?\"}"; 19 | NewCommentDto expectedCommentDto = new NewCommentDto("Do they serve lactose-free latte there?"); 20 | 21 | assertThat(jacksonTester.parse(jsonValue)).usingRecursiveComparison().isEqualTo(expectedCommentDto); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/repository/EventRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | import ru.practicum.event.model.Event; 8 | import ru.practicum.event.model.enums.EventState; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.Set; 13 | 14 | @Repository 15 | public interface EventRepository extends JpaRepository { 16 | 17 | boolean existsByCategoryId(Long categoryId); 18 | 19 | Page findAllByInitiatorId(Long userId, Pageable pageable); 20 | 21 | List findAllByIdIn(Set events); 22 | 23 | Optional findByIdAndStateIs(Long eventId, EventState published); 24 | 25 | Optional findByIdAndInitiatorId(Long eventId, Long userId); 26 | } -------------------------------------------------------------------------------- /main-service/src/main/resources/static/js/header-script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function() { 2 | let profileDropdown = document.querySelector('.profile-dropdown'); 3 | let profileHeaderBtn = document.querySelector('.account-navbar-link'); 4 | 5 | let dropdown = false; 6 | 7 | profileHeaderBtn.addEventListener('mouseover', function() { 8 | if (!dropdown) { 9 | dropdown = true; 10 | displayDropdown(profileDropdown, true); 11 | } 12 | }); 13 | 14 | profileHeaderBtn.addEventListener('mouseout', function() { 15 | if (dropdown) { 16 | dropdown = false; 17 | setTimeout(() => displayDropdown(profileDropdown, dropdown), 200); 18 | } 19 | }); 20 | }); 21 | 22 | function displayDropdown (profileDropdown, showBar) { 23 | if (showBar) { 24 | profileDropdown.classList.add('active'); 25 | } else { 26 | profileDropdown.classList.remove('active'); 27 | } 28 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.factory.Mappers; 6 | import ru.practicum.comment.dto.CommentDto; 7 | import ru.practicum.comment.dto.NewCommentDto; 8 | import ru.practicum.comment.model.Comment; 9 | 10 | import java.util.List; 11 | 12 | @Mapper(componentModel = "spring") 13 | public interface CommentMapper { 14 | CommentMapper INSTANCE = Mappers.getMapper(CommentMapper.class); 15 | 16 | @Mapping(target = "id", ignore = true) 17 | @Mapping(target = "createdOn", ignore = true) 18 | @Mapping(target = "user", ignore = true) 19 | @Mapping(target = "status", ignore = true) 20 | @Mapping(target = "event", ignore = true) 21 | Comment newCommentDtoToComment(NewCommentDto newCommentDto); 22 | 23 | CommentDto commentToCommentDto(Comment comment); 24 | 25 | List listCommentToListCommentDto(List comment); 26 | } 27 | -------------------------------------------------------------------------------- /stats-service/server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 2 | spring.jpa.hibernate.ddl-auto=update 3 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect 4 | spring.jpa.properties.hibernate.format_sql=true 5 | spring.sql.init.mode=always 6 | #--- 7 | #spring.datasource.driverClassName=org.postgresql.Driver 8 | #spring.datasource.url=${SPRING_DATASOURCE_URL} 9 | #spring.datasource.username=${POSTGRES_USER} 10 | #spring.datasource.password=${POSTGRES_PASSWORD} 11 | ##--- 12 | spring.config.activate.on-profile=test 13 | spring.datasource.driverClassName=org.h2.Driver 14 | spring.datasource.url=jdbc:h2:mem:explore-with-me 15 | spring.datasource.username=owner 16 | spring.datasource.password=password 17 | spring.h2.console.enabled=true 18 | #--- 19 | #spring.config.activate.on-profile=postgres 20 | #spring.datasource.driverClassName=org.postgresql.Driver 21 | #spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 22 | #spring.datasource.username=postgres 23 | #spring.datasource.password=postgres 24 | ##--- -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/dto/NewUserDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import javax.persistence.Transient; 9 | import javax.validation.constraints.Email; 10 | import javax.validation.constraints.NotBlank; 11 | import javax.validation.constraints.NotNull; 12 | import javax.validation.constraints.Size; 13 | 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class NewUserDto { 18 | 19 | @NotNull 20 | @NotBlank 21 | @Length(min = 2, max = 250) 22 | private String name; 23 | 24 | @NotNull 25 | @Size(min = 5) 26 | private String username; 27 | 28 | @NotNull 29 | @NotBlank 30 | @Email 31 | @Length(min = 6, max = 254) 32 | private String email; 33 | 34 | @NotNull 35 | @Size(min = 5) 36 | private String password; 37 | 38 | @NotNull 39 | @Transient 40 | private String passwordConfirm; 41 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/EventShortDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import ru.practicum.category.dto.CategoryDto; 8 | import ru.practicum.user.dto.UserShortDto; 9 | 10 | import javax.validation.constraints.Size; 11 | import java.time.LocalDateTime; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class EventShortDto { 17 | 18 | @Size(min = 20, max = 2000) 19 | private String annotation; 20 | 21 | private Long id; 22 | 23 | private CategoryDto category; 24 | 25 | private Long confirmedRequests; 26 | 27 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 28 | private LocalDateTime eventDate; 29 | 30 | private UserShortDto initiator; 31 | 32 | private Boolean paid; 33 | 34 | @Size(min = 3, max = 120) 35 | private String title; 36 | 37 | private Long views; 38 | } 39 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/service/RequestService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.service; 2 | 3 | import ru.practicum.event.dto.EventRequestStatusUpdateRequest; 4 | import ru.practicum.event.model.Event; 5 | import ru.practicum.request.model.ParticipationRequest; 6 | 7 | import javax.validation.Valid; 8 | import java.util.List; 9 | 10 | public interface RequestService { 11 | ParticipationRequest addParticipationRequest(Long userId, Long eventId); 12 | 13 | ParticipationRequest cancelParticipationRequest(Long userId, Long requestId); 14 | 15 | List getUserRequests(Long userId); 16 | 17 | List getUserEventRequests(Long userId, Long eventId); 18 | 19 | List getEventRequests(Long eventId, @Valid EventRequestStatusUpdateRequest requestsUpdate); 20 | 21 | Event checkParticipationRequest(Long userId, Long eventId); 22 | 23 | void updateEventRequests(Event event, @Valid EventRequestStatusUpdateRequest requestsUpdate, List participationRequests); 24 | } 25 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/comment/dto/CommentStatusUpdateRequestTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 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.comment.model.CommentStatus; 8 | 9 | import java.util.List; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | @JsonTest 14 | public class CommentStatusUpdateRequestTest { 15 | 16 | @Autowired 17 | private JacksonTester jacksonTester; 18 | 19 | @Test 20 | void testDeserialize() throws Exception { 21 | String jsonValue = "{\"commentIds\": [1, 2],\"status\": \"PUBLISHED\"}"; 22 | CommentStatusUpdateRequest expectedUpdateRequest = new CommentStatusUpdateRequest(List.of(1L, 2L), CommentStatus.PUBLISHED); 23 | 24 | assertThat(jacksonTester.parse(jsonValue)).usingRecursiveComparison().isEqualTo(expectedUpdateRequest); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.factory.Mappers; 7 | import ru.practicum.compilation.dto.CompilationDto; 8 | import ru.practicum.compilation.dto.NewCompilationDto; 9 | import ru.practicum.compilation.model.Compilation; 10 | import ru.practicum.event.mapper.EventMapper; 11 | 12 | import java.util.List; 13 | 14 | @Mapper(uses = EventMapper.class, componentModel = MappingConstants.ComponentModel.SPRING) 15 | public interface CompilationMapper { 16 | 17 | CompilationMapper INSTANCE = Mappers.getMapper(CompilationMapper.class); 18 | 19 | @Mapping(target = "id", ignore = true) 20 | @Mapping(target = "events", ignore = true) 21 | Compilation newCompilationDtoToCompilation(NewCompilationDto compilationDto); 22 | 23 | CompilationDto compilationToCompilationDto(Compilation compilation); 24 | 25 | List listCompilationToListCompilationDto(List compilation); 26 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/dto/CommentDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import ru.practicum.event.dto.EventShortDto; 8 | import ru.practicum.user.dto.UserShortDto; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.JoinColumn; 12 | import javax.persistence.ManyToOne; 13 | import javax.validation.constraints.NotBlank; 14 | import javax.validation.constraints.Size; 15 | import java.time.LocalDateTime; 16 | 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class CommentDto { 21 | 22 | @NotBlank 23 | @Size(min = 5, max = 5000) 24 | private String text; 25 | 26 | @Column(name = "created_on") 27 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 28 | private LocalDateTime createdOn; 29 | 30 | @ManyToOne 31 | @JoinColumn(name = "user_id") 32 | private UserShortDto user; 33 | 34 | @ManyToOne 35 | @JoinColumn(name = "event_id") 36 | private EventShortDto event; 37 | } 38 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/repository/CommentRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | import ru.practicum.comment.model.Comment; 6 | import ru.practicum.comment.model.CommentStatus; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Repository 12 | public interface CommentRepository extends JpaRepository { 13 | 14 | Optional findByIdAndUserIdAndEventId(Long commentId, Long userId, Long eventId); 15 | 16 | List findAllByUserIdAndStatus(Long userId, CommentStatus published); 17 | 18 | List findAllByUserIdAndEventIdAndStatus(Long userId, Long eventId, CommentStatus published); 19 | 20 | void deleteByIdAndUserIdAndEventIdAndStatus(Long commentId, Long userId, Long eventId, CommentStatus published); 21 | 22 | void deleteAllByIdIn(List commentId); 23 | 24 | List findAllByIdInAndStatus(List commentIds, CommentStatus status); 25 | 26 | boolean existsByIdAndUserIdAndEventIdAndStatus(Long commentId, Long userId, Long eventId, CommentStatus published); 27 | } 28 | -------------------------------------------------------------------------------- /stats-service/dto/src/main/java/ru/practicum/RequestDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.validator.constraints.Length; 9 | 10 | import javax.validation.constraints.NotBlank; 11 | import javax.validation.constraints.NotNull; 12 | import java.time.LocalDateTime; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class RequestDto { 18 | 19 | private Integer id; 20 | 21 | @NotNull 22 | @NotBlank 23 | private String app; 24 | 25 | @NotBlank 26 | private String uri; 27 | 28 | @NotNull 29 | @NotBlank 30 | @Length(min = 7, max = 15) 31 | private String ip; 32 | 33 | @NotNull 34 | @JsonProperty("timestamp") 35 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 36 | private LocalDateTime timestamp; 37 | 38 | public RequestDto(String app, String uri, String ip, LocalDateTime timestamp) { 39 | this.app = app; 40 | this.uri = uri; 41 | this.ip = ip; 42 | this.timestamp = timestamp; 43 | } 44 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/model/Role.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import org.springframework.security.core.GrantedAuthority; 10 | 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.GenerationType; 14 | import javax.persistence.Id; 15 | import javax.persistence.ManyToMany; 16 | import javax.persistence.Table; 17 | import javax.persistence.Transient; 18 | import java.util.Set; 19 | 20 | @Entity 21 | @Table(name = "roles") 22 | @Getter 23 | @Setter 24 | @ToString 25 | @EqualsAndHashCode(of = "id") 26 | @NoArgsConstructor 27 | @AllArgsConstructor 28 | public class Role implements GrantedAuthority { 29 | 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.IDENTITY) 32 | private Long id; 33 | 34 | private String name; 35 | 36 | @Transient 37 | @ManyToMany(mappedBy = "user_roles") 38 | private Set users; 39 | 40 | @Override 41 | public String getAuthority() { 42 | return getName(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/model/Request.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.CascadeType; 9 | import javax.persistence.Column; 10 | import javax.persistence.Entity; 11 | import javax.persistence.GeneratedValue; 12 | import javax.persistence.GenerationType; 13 | import javax.persistence.Id; 14 | import javax.persistence.JoinColumn; 15 | import javax.persistence.ManyToOne; 16 | import javax.persistence.Table; 17 | import javax.validation.constraints.NotNull; 18 | import java.time.LocalDateTime; 19 | 20 | @Entity 21 | @Table(name = "requests") 22 | @Getter 23 | @Setter 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | public class Request { 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | private Integer id; 30 | 31 | @ManyToOne(cascade = CascadeType.ALL) 32 | @JoinColumn(name = "app_id") 33 | private App app; 34 | 35 | @NotNull 36 | private String uri; 37 | 38 | @NotNull 39 | private String ip; 40 | 41 | @NotNull 42 | @Column(name = "time_stamp") 43 | private LocalDateTime timestamp; 44 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.service; 2 | 3 | import ru.practicum.comment.dto.CommentStatusUpdateRequest; 4 | import ru.practicum.comment.dto.NewCommentDto; 5 | import ru.practicum.comment.model.Comment; 6 | import ru.practicum.comment.model.CommentStatus; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | 11 | public interface CommentService { 12 | 13 | Comment addUserComment(Long userId, Long eventId, NewCommentDto newCommentDto); 14 | 15 | Comment updateUserComment(Long userId, Long eventId, Long commentId, NewCommentDto newCommentDto); 16 | 17 | void deleteUserComment(Long userId, Long eventId, Long commentId); 18 | 19 | Comment getUserEventComment(Long userId, Long eventId, Long commentId); 20 | 21 | List getAllUserComments(Long userId); 22 | 23 | List getAllUserEventComments(Long userId, Long eventId); 24 | 25 | List getAdminComments(String text, List users, List statuses, List events, LocalDateTime rangeStart, LocalDateTime rangeEnd, Integer from, Integer size); 26 | 27 | List moderateAdminComments(CommentStatusUpdateRequest updateRequest); 28 | } 29 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/category/mapper/CategoryMapperTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.mapper; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import ru.practicum.category.dto.CategoryDto; 5 | import ru.practicum.category.dto.NewCategoryDto; 6 | import ru.practicum.category.model.Category; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertNotNull; 10 | 11 | public class CategoryMapperTest { 12 | 13 | @Test 14 | void newCategoryDtoToCategory() { 15 | NewCategoryDto categoryDto = new NewCategoryDto("Meeting new friends"); 16 | Category category = CategoryMapper.INSTANCE.newCategoryDtoToCategory(categoryDto); 17 | 18 | assertNotNull(category); 19 | assertEquals(category.getName(), categoryDto.getName()); 20 | } 21 | 22 | @Test 23 | void categoryToCategoryDto() { 24 | Category category = new Category(1L, "Meeting new friends"); 25 | CategoryDto categoryDto = CategoryMapper.INSTANCE.categoryToCategoryDto(category); 26 | 27 | assertNotNull(categoryDto); 28 | assertEquals(category.getId(), categoryDto.getId()); 29 | assertEquals(category.getName(), categoryDto.getName()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/web/AuthControllerTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.web.controller; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.web.servlet.MockMvc; 8 | 9 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 10 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 12 | 13 | @AutoConfigureMockMvc 14 | @SpringBootTest 15 | public class AuthControllerTest { 16 | 17 | @Autowired 18 | private MockMvc mvc; 19 | 20 | @Test 21 | public void getSignUp_shouldSucceedWith200() throws Exception { 22 | mvc.perform(get("/sign-up")).andDo(print()) 23 | .andExpect(view().name("sign-up")); 24 | } 25 | 26 | @Test 27 | public void getLogin_shouldSucceedWith200() throws Exception { 28 | mvc.perform(get("/sign-in")).andDo(print()) 29 | .andExpect(view().name("sign-in")); 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /main-service/src/main/resources/templates/sign-in.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Explore with me - Войти в аккаунт 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

Войти / Регистрация

19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/EventUpdateDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import ru.practicum.event.model.Location; 8 | import ru.practicum.event.model.enums.StateAction; 9 | 10 | import javax.persistence.EnumType; 11 | import javax.persistence.Enumerated; 12 | import javax.validation.constraints.Future; 13 | import javax.validation.constraints.Size; 14 | import java.time.LocalDateTime; 15 | 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class EventUpdateDto { 20 | 21 | @Size(min = 20, max = 2000) 22 | private String annotation; 23 | 24 | private Long category; 25 | 26 | @Size(min = 20, max = 7000) 27 | private String description; 28 | 29 | @Future 30 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 31 | private LocalDateTime eventDate; 32 | 33 | private Location location; 34 | 35 | private Boolean paid; 36 | 37 | private Long participantLimit; 38 | 39 | private Boolean requestModeration; 40 | 41 | @Enumerated(EnumType.STRING) 42 | private StateAction stateAction; 43 | 44 | @Size(min = 3, max = 120) 45 | private String title; 46 | } 47 | -------------------------------------------------------------------------------- /main-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | #data.sql 3 | spring.jpa.defer-datasource-initialization=true 4 | 5 | spring.jpa.hibernate.ddl-auto=update 6 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect 7 | spring.jpa.properties.hibernate.format_sql=true 8 | spring.sql.init.mode=always 9 | #--- 10 | #spring.datasource.driverClassName=org.postgresql.Driver 11 | #spring.datasource.url=${SPRING_DATASOURCE_URL} 12 | #spring.datasource.username=${POSTGRES_USER} 13 | #spring.datasource.password=${POSTGRES_PASSWORD} 14 | #--- 15 | spring.config.activate.on-profile=ci,test 16 | spring.datasource.driverClassName=org.h2.Driver 17 | spring.datasource.url=jdbc:h2:mem:explore-with-me;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL;INIT=CREATE SCHEMA IF NOT EXISTS DUMMY 18 | spring.datasource.username=test 19 | spring.datasource.password=test 20 | #--- 21 | #spring.config.activate.on-profile=postgres 22 | #spring.datasource.driverClassName=org.postgresql.Driver 23 | #spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 24 | #spring.datasource.username=postgres 25 | #spring.datasource.password=postgres 26 | #--- 27 | logging.level.org.hibernate.SQL=debug 28 | logging.level.org.springframework=debug 29 | logging.level.com=debug 30 | logging.level.org.springframework.security=debug 31 | 32 | 33 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/category/dto/NewCategoryDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.dto; 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 org.springframework.boot.test.json.JsonContent; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @JsonTest 12 | public class NewCategoryDtoTest { 13 | 14 | @Autowired 15 | private JacksonTester jacksonTester; 16 | 17 | @Test 18 | void testSerialize() throws Exception { 19 | NewCategoryDto categoryDto = new NewCategoryDto("Meeting new friends."); 20 | JsonContent categoryDtoSaved = jacksonTester.write(categoryDto); 21 | 22 | assertThat(categoryDtoSaved).hasJsonPath("$.name"); 23 | assertThat(categoryDtoSaved).extractingJsonPathStringValue("$.name").isEqualTo(categoryDto.getName()); 24 | } 25 | 26 | @Test 27 | void testDeserialize() throws Exception { 28 | String jsonValue = "{\"name\":\"Meeting new friends.\"}"; 29 | NewCategoryDto expectedCategoryDto = new NewCategoryDto("Meeting new friends."); 30 | 31 | assertThat(jacksonTester.parse(jsonValue)).usingRecursiveComparison().isEqualTo(expectedCategoryDto); 32 | } 33 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/service/EventService.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.service; 2 | 3 | import ru.practicum.event.dto.EventUpdateDto; 4 | import ru.practicum.event.dto.NewEventDto; 5 | import ru.practicum.event.model.Event; 6 | import ru.practicum.event.model.enums.EventSort; 7 | import ru.practicum.event.model.enums.EventState; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | 13 | public interface EventService { 14 | List getAdminEvents(List users, List states, List categories, 15 | LocalDateTime rangeStart, LocalDateTime rangeEnd, Integer from, Integer size); 16 | 17 | Event updateAdminEvent(Long eventId, EventUpdateDto eventUpdateDto); 18 | 19 | List getAll(String text, List categories, Boolean paid, LocalDateTime rangeStart, 20 | LocalDateTime rangeEnd, Boolean onlyAvailable, Integer from, Integer size, 21 | EventSort sort, HttpServletRequest request); 22 | 23 | Event get(Long id, HttpServletRequest request); 24 | 25 | List getUserEvents(Long userId, Integer from, Integer size); 26 | 27 | Event addUserEvent(Long userId, NewEventDto event); 28 | 29 | Event getUserEventById(Long userId, Long eventId); 30 | 31 | Event updateUserEventById(Long userId, Long eventId, EventUpdateDto eventDto); 32 | } 33 | -------------------------------------------------------------------------------- /stats-service/client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | ru.practicum 9 | stats-service 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | client 14 | jar 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-webflux 30 | 31 | 32 | 33 | ru.practicum 34 | dto 35 | 0.0.1-SNAPSHOT 36 | compile 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/model/Compilation.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | import ru.practicum.event.model.Event; 9 | 10 | import javax.persistence.CascadeType; 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.GenerationType; 14 | import javax.persistence.Id; 15 | import javax.persistence.JoinColumn; 16 | import javax.persistence.JoinTable; 17 | import javax.persistence.ManyToMany; 18 | import javax.persistence.Table; 19 | import javax.validation.constraints.Size; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | @Entity 24 | @Table(name = "compilations") 25 | @Getter 26 | @Setter 27 | @ToString 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | public class Compilation { 31 | 32 | @Id 33 | @GeneratedValue(strategy = GenerationType.IDENTITY) 34 | private Long id; 35 | 36 | private Boolean pinned; 37 | 38 | @Size(min = 3, max = 50) 39 | private String title; 40 | 41 | @ManyToMany(cascade = CascadeType.ALL) 42 | @JoinTable( 43 | name = "comp_events", 44 | joinColumns = @JoinColumn(name = "compilation_id"), 45 | inverseJoinColumns = @JoinColumn(name = "event_id")) 46 | private List events = new ArrayList<>(); 47 | } 48 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/NewEventDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | import ru.practicum.event.model.Location; 9 | 10 | import javax.validation.constraints.Future; 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.PositiveOrZero; 14 | import javax.validation.constraints.Size; 15 | import java.time.LocalDateTime; 16 | 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class NewEventDto { 21 | 22 | @NotNull 23 | @Size(min = 20, max = 2000) 24 | private String annotation; 25 | 26 | @NotNull 27 | private Long category; 28 | 29 | @NotNull 30 | @Size(min = 20, max = 7000) 31 | private String description; 32 | 33 | @NotNull 34 | @Future 35 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 36 | @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") 37 | private LocalDateTime eventDate; 38 | 39 | @NotNull 40 | private Location location; 41 | 42 | private Boolean paid; 43 | 44 | @PositiveOrZero 45 | private Integer participantLimit; 46 | 47 | private Boolean requestModeration; 48 | 49 | @NotBlank 50 | @Size(min = 3, max = 120) 51 | private String title; 52 | } 53 | -------------------------------------------------------------------------------- /stats-service/server/src/main/test/java/ru/practicum/dto/RequestOutputDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.dto; 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 org.springframework.boot.test.json.JsonContent; 8 | import ru.practicum.RequestOutputDto; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | @JsonTest 13 | public class RequestOutputDtoTest { 14 | 15 | @Autowired 16 | private JacksonTester jacksonTester; 17 | 18 | @Test 19 | void testSerialize() throws Exception { 20 | RequestOutputDto requestOutputDto = new RequestOutputDto("ewm-main-service", "/events/1", 2L); 21 | 22 | JsonContent requestOutputDtoSaved = jacksonTester.write(requestOutputDto); 23 | 24 | assertThat(requestOutputDtoSaved).hasJsonPath("$.app"); 25 | assertThat(requestOutputDtoSaved).hasJsonPath("$.uri"); 26 | assertThat(requestOutputDtoSaved).hasJsonPath("$.hits"); 27 | 28 | assertThat(requestOutputDtoSaved).extractingJsonPathStringValue("$.app").isEqualTo(requestOutputDto.getApp()); 29 | assertThat(requestOutputDtoSaved).extractingJsonPathStringValue("$.uri").isEqualTo(requestOutputDto.getUri()); 30 | assertThat(requestOutputDtoSaved).extractingJsonPathNumberValue("$.hits").isEqualTo(requestOutputDto.getHits().intValue()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.MappingConstants; 6 | import org.mapstruct.factory.Mappers; 7 | import ru.practicum.event.dto.EventFullDto; 8 | import ru.practicum.event.dto.EventShortDto; 9 | import ru.practicum.event.dto.NewEventDto; 10 | import ru.practicum.event.model.Event; 11 | 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | @Mapper(componentModel = MappingConstants.ComponentModel.SPRING) 16 | public interface EventMapper { 17 | EventMapper INSTANCE = Mappers.getMapper(EventMapper.class); 18 | 19 | EventFullDto eventToEventFullDto(Event event); 20 | 21 | EventShortDto eventToEventShortDto(Event event); 22 | 23 | List listEventToListEventShortDto(List event); 24 | 25 | List listEventToListEventFullDto(List event); 26 | 27 | @Mapping(target = "id", ignore = true) 28 | @Mapping(target = "confirmedRequests", ignore = true) 29 | @Mapping(target = "createdOn", ignore = true) 30 | @Mapping(target = "initiator", ignore = true) 31 | @Mapping(target = "category", ignore = true) 32 | @Mapping(target = "publishedOn", ignore = true) 33 | @Mapping(target = "state", ignore = true) 34 | @Mapping(target = "views", ignore = true) 35 | @Mapping(target = "compilations", ignore = true) 36 | Event newEventDtoToEvent(NewEventDto eventDto); 37 | 38 | Set listEventToSetEventShortDto(List events); 39 | 40 | } -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/category/dto/CategoryDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.dto; 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 org.springframework.boot.test.json.JsonContent; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @JsonTest 12 | public class CategoryDtoTest { 13 | 14 | @Autowired 15 | private JacksonTester jacksonTester; 16 | 17 | @Test 18 | void testSerialize() throws Exception { 19 | CategoryDto categoryDto = new CategoryDto("Meeting new friends.", 1L); 20 | JsonContent categoryDtoSaved = jacksonTester.write(categoryDto); 21 | 22 | assertThat(categoryDtoSaved).hasJsonPath("$.id"); 23 | assertThat(categoryDtoSaved).hasJsonPath("$.name"); 24 | 25 | assertThat(categoryDtoSaved).extractingJsonPathNumberValue("$.id").isEqualTo(categoryDto.getId().intValue()); 26 | assertThat(categoryDtoSaved).extractingJsonPathStringValue("$.name").isEqualTo(categoryDto.getName()); 27 | } 28 | 29 | @Test 30 | void testDeserialize() throws Exception { 31 | String jsonValue = "{\"name\":\"Meeting new friends.\",\"id\":\"1\"}"; 32 | CategoryDto expectedCategoryDto = new CategoryDto("Meeting new friends.", 1L); 33 | 34 | assertThat(jacksonTester.parse(jsonValue)).usingRecursiveComparison().isEqualTo(expectedCategoryDto); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/css/user-settings.css: -------------------------------------------------------------------------------- 1 | .content-container { 2 | display: block; 3 | max-height: 100vh; 4 | width: 40%; 5 | margin: 100px 15% 60px; 6 | padding: 30px 0; 7 | } 8 | 9 | .add-event-header { 10 | display: block; 11 | font-size: 28px; 12 | font-weight: 700; 13 | margin-bottom: 40px; 14 | } 15 | 16 | .add-event-content-block:last-child { 17 | display: block; 18 | margin-top: 40px; 19 | } 20 | 21 | .profile-settings-secondary-header { 22 | display: block; 23 | font-size: 24px; 24 | font-weight: 600; 25 | margin-bottom: 30px; 26 | } 27 | 28 | .add-event-fields { 29 | margin: 20px 0; 30 | } 31 | 32 | .add-event-field { 33 | display: grid; 34 | align-items: center; 35 | grid-template-columns: 250px auto; 36 | /*justify-items: start;*/ 37 | /*justify-items: center;*/ 38 | margin: 15px 0; 39 | } 40 | 41 | .add-event-label { 42 | font-size: 16px; 43 | font-weight: 500; 44 | } 45 | 46 | .add-event-input { 47 | height: 26px; 48 | width: auto; 49 | background: var(--form-background); 50 | border: var(--accent-background-color) 2px solid; 51 | border-radius: 10px; 52 | padding: 3px 12px; 53 | } 54 | 55 | .add-event-btn { 56 | margin-top: 30px; 57 | background: var(--accent-color); 58 | padding: 10px 30px; 59 | font-size: 16px; 60 | font-weight: 600; 61 | color: #FFF; 62 | text-decoration: none; 63 | border-radius: 10px; 64 | cursor: pointer; 65 | } 66 | 67 | .profile-settings-warning { 68 | background: #ffc5b8; 69 | padding: 20px; 70 | margin-top: 10px; 71 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/model/Comment.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.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 lombok.ToString; 9 | import ru.practicum.event.model.Event; 10 | import ru.practicum.user.model.User; 11 | 12 | import javax.persistence.Column; 13 | import javax.persistence.Entity; 14 | import javax.persistence.EnumType; 15 | import javax.persistence.Enumerated; 16 | import javax.persistence.GeneratedValue; 17 | import javax.persistence.GenerationType; 18 | import javax.persistence.Id; 19 | import javax.persistence.JoinColumn; 20 | import javax.persistence.ManyToOne; 21 | import javax.persistence.Table; 22 | import javax.validation.constraints.NotBlank; 23 | import javax.validation.constraints.Size; 24 | import java.time.LocalDateTime; 25 | 26 | @Entity 27 | @Table(name = "comments") 28 | @Getter 29 | @Setter 30 | @ToString 31 | @NoArgsConstructor 32 | @AllArgsConstructor 33 | public class Comment { 34 | 35 | @Id 36 | @GeneratedValue(strategy = GenerationType.IDENTITY) 37 | private Long id; 38 | 39 | @NotBlank 40 | @Size(min = 5, max = 5000) 41 | private String text; 42 | 43 | @Column(name = "created") 44 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 45 | private LocalDateTime createdOn; 46 | 47 | @ManyToOne 48 | @JoinColumn(name = "user_id") 49 | private User user; 50 | 51 | @Enumerated(EnumType.STRING) 52 | private CommentStatus status; 53 | 54 | @ManyToOne 55 | @JoinColumn(name = "event_id") 56 | private Event event; 57 | } 58 | -------------------------------------------------------------------------------- /main-service/src/main/resources/templates/sign-up.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Explore with me - Регистрация 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

Регистрация / Войти

19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/security/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.security; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.security.web.SecurityFilterChain; 10 | 11 | @EnableWebSecurity 12 | @RequiredArgsConstructor 13 | public class WebSecurityConfig { 14 | 15 | @Bean 16 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 17 | http 18 | .csrf() 19 | .disable() 20 | .authorizeRequests() 21 | .antMatchers("/sign-up", "/sign-in").not().fullyAuthenticated() 22 | .antMatchers("/admin/**").hasRole("ADMIN") 23 | .antMatchers("/users/**").hasRole("USER") 24 | .antMatchers("/", "/resources/**", "/js/**", "/css/**", "/images/**").permitAll() 25 | .anyRequest().authenticated() 26 | .and() 27 | .formLogin() 28 | .loginPage("/sign-in") 29 | .defaultSuccessUrl("/") 30 | .permitAll() 31 | .and() 32 | .logout() 33 | .permitAll() 34 | .logoutSuccessUrl("/"); 35 | 36 | return http.build(); 37 | } 38 | 39 | @Bean 40 | public PasswordEncoder passwordEncoder() { 41 | return new BCryptPasswordEncoder(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/dto/EventFullDto.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import ru.practicum.category.dto.CategoryDto; 8 | import ru.practicum.event.model.Location; 9 | import ru.practicum.event.model.enums.EventState; 10 | import ru.practicum.user.dto.UserShortDto; 11 | 12 | import javax.persistence.EnumType; 13 | import javax.persistence.Enumerated; 14 | import javax.validation.constraints.Size; 15 | import java.time.LocalDateTime; 16 | 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class EventFullDto { 21 | 22 | private Long id; 23 | 24 | @Size(min = 3, max = 120) 25 | private String title; 26 | 27 | @Size(min = 20, max = 2000) 28 | private String annotation; 29 | 30 | private CategoryDto category; 31 | 32 | private Long confirmedRequests; 33 | 34 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 35 | private LocalDateTime createdOn; 36 | 37 | @Size(min = 20, max = 7000) 38 | private String description; 39 | 40 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 41 | private LocalDateTime eventDate; 42 | 43 | private UserShortDto initiator; 44 | 45 | private Location location; 46 | 47 | private Boolean paid; 48 | 49 | private Long participantLimit; 50 | 51 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 52 | private LocalDateTime publishedOn; 53 | 54 | private Boolean requestModeration; 55 | 56 | @Enumerated(EnumType.STRING) 57 | private EventState state; 58 | 59 | private Long views; 60 | } 61 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/model/ParticipationRequest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.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 lombok.ToString; 9 | import ru.practicum.event.model.Event; 10 | import ru.practicum.user.model.User; 11 | 12 | import javax.persistence.Entity; 13 | import javax.persistence.EnumType; 14 | import javax.persistence.Enumerated; 15 | import javax.persistence.GeneratedValue; 16 | import javax.persistence.GenerationType; 17 | import javax.persistence.Id; 18 | import javax.persistence.JoinColumn; 19 | import javax.persistence.ManyToOne; 20 | import javax.persistence.Table; 21 | import java.time.LocalDateTime; 22 | 23 | @Entity 24 | @Table(name = "requests") 25 | @Getter 26 | @Setter 27 | @ToString 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | public class ParticipationRequest { 31 | 32 | @Id 33 | @GeneratedValue(strategy = GenerationType.IDENTITY) 34 | private Long id; 35 | 36 | @ManyToOne 37 | @JoinColumn(name = "requester_id", nullable = false) 38 | private User requester; 39 | 40 | @ManyToOne 41 | @JoinColumn(name = "event_id", nullable = false) 42 | private Event event; 43 | 44 | @Enumerated(EnumType.STRING) 45 | private ParticipationStatus status; 46 | 47 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 48 | private LocalDateTime created; 49 | 50 | public ParticipationRequest(User requester, Event event, ParticipationStatus status, LocalDateTime created) { 51 | this.requester = requester; 52 | this.event = event; 53 | this.status = status; 54 | this.created = created; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/aspect/WebControllerLogger.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.aspect; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.JoinPoint; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.aspectj.lang.annotation.Before; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.util.Arrays; 14 | import java.util.Set; 15 | import java.util.stream.Collectors; 16 | 17 | @Component 18 | @Aspect 19 | @Slf4j 20 | public class WebControllerLogger { 21 | 22 | @Pointcut("within(ru.practicum.web..*)") 23 | public void serviceMethod() { } 24 | 25 | @Before("serviceMethod()") 26 | public void callLogService(JoinPoint joinPoint) { 27 | String methodName = joinPoint.getSignature().getName(); 28 | String className = joinPoint.getSignature().getDeclaringTypeName(); 29 | Object[] methodArgs = joinPoint.getArgs(); 30 | log.info(""); 31 | log.info("-------------------------------------------------------------------------"); 32 | log.info(""); 33 | log.info("Calling class " + className + ", method " + methodName + " with args:"); 34 | Arrays.stream(methodArgs).forEach(e -> log.info("ARGUMENT: " + e)); 35 | 36 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 37 | 38 | Set roles = authentication.getAuthorities().stream() 39 | .map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); 40 | 41 | log.info("Current user roles: " + roles); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/exception/MainServiceExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.exception; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.http.HttpStatus; 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 | 9 | import java.time.LocalDateTime; 10 | 11 | @RestControllerAdvice 12 | @Slf4j 13 | public class MainServiceExceptionHandler { 14 | 15 | @ExceptionHandler(ObjectNotFoundException.class) 16 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 17 | public ApiError exceptionNotFound(ObjectNotFoundException e) { 18 | String reason = "The required object was not found."; 19 | log.error(e.getMessage()); 20 | return new ApiError(HttpStatus.NOT_FOUND.toString(), reason, e.getMessage(), LocalDateTime.now()); 21 | } 22 | 23 | @ExceptionHandler({RequestConflictException.class, SQLConstraintViolationException.class}) 24 | @ResponseStatus(value = HttpStatus.CONFLICT) 25 | public ApiError exceptionConflict(RuntimeException e) { 26 | String reason = "Integrity constraint has been violated."; 27 | log.error(e.getMessage()); 28 | return new ApiError(HttpStatus.CONFLICT.toString(), reason, e.getMessage(), LocalDateTime.now()); 29 | } 30 | 31 | @ExceptionHandler({IncorrectRequestException.class, ConditionsNotMetException.class}) 32 | @ResponseStatus(value = HttpStatus.BAD_REQUEST) 33 | public ApiError exceptionBadRequest(RuntimeException e) { 34 | String reason = "Incorrectly made request."; 35 | log.error(e.getMessage()); 36 | return new ApiError(HttpStatus.BAD_REQUEST.toString(), reason, e.getMessage(), LocalDateTime.now()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/controller/PublicCategoryController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.ResponseStatus; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import ru.practicum.category.dto.CategoryDto; 13 | import ru.practicum.category.mapper.CategoryMapper; 14 | import ru.practicum.category.service.CategoryService; 15 | 16 | import java.util.List; 17 | 18 | @RestController 19 | @RequestMapping("/categories") 20 | @RequiredArgsConstructor 21 | @Slf4j 22 | public class PublicCategoryController { 23 | private final CategoryService categoryService; 24 | private final CategoryMapper categoryMapper; 25 | 26 | @GetMapping 27 | @ResponseStatus(HttpStatus.OK) 28 | public List get(@RequestParam(defaultValue = "0") Integer from, 29 | @RequestParam(defaultValue = "10") Integer size) { 30 | log.info("Calling GET: /categories with 'from': {}, 'size': {}", from, size); 31 | return categoryMapper.listCategoryToListCategoryDto(categoryService.getAll(from, size)); 32 | } 33 | 34 | @GetMapping("/{categoryId}") 35 | @ResponseStatus(HttpStatus.OK) 36 | public CategoryDto get(@PathVariable Long categoryId) { 37 | log.info("Calling GET: /categories/{categoryId} with 'categoryId': {}", categoryId); 38 | return categoryMapper.categoryToCategoryDto(categoryService.get(categoryId)); 39 | } 40 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | stats-server: 4 | restart: on-failure 5 | build: ./stats-service/server 6 | image: ewm-stats-server 7 | container_name: "ewm-stats-server" 8 | ports: 9 | - "9090:9090" 10 | depends_on: 11 | - stats-db 12 | environment: 13 | - SPRING_DATASOURCE_URL=jdbc:postgresql://stats-db:5432/stats_db 14 | - POSTGRES_USER=owner 15 | - POSTGRES_PASSWORD=password 16 | - DB_NAME=stats_db 17 | - SPRING_DATASOURCE_USERNAME=owner 18 | - SPRING_DATASOURCE_PASSWORD=password 19 | - DB_HOST=stats-db 20 | - DB_PORT=6541 21 | 22 | stats-db: 23 | image: postgres:14-alpine 24 | container_name: "stats-db" 25 | ports: 26 | - "6541:5432" 27 | volumes: 28 | - /var/lib/postgresql/stats-service 29 | environment: 30 | - POSTGRES_DB=stats_db 31 | - POSTGRES_USER=owner 32 | - POSTGRES_PASSWORD=password 33 | 34 | ewm-service: 35 | restart: on-failure 36 | build: ./main-service 37 | image: ewm-main-service 38 | container_name: "ewm-main-service" 39 | ports: 40 | - "8080:8080" 41 | depends_on: 42 | - main-db 43 | - stats-server 44 | environment: 45 | - SPRING_DATASOURCE_URL=jdbc:postgresql://main-db:5432/main_db 46 | - POSTGRES_USER=owner 47 | - POSTGRES_PASSWORD=password 48 | - DB_NAME=main_db 49 | - SPRING_DATASOURCE_USERNAME=owner 50 | - SPRING_DATASOURCE_PASSWORD=password 51 | - DB_HOST=main-db 52 | - DB_PORT=6542 53 | 54 | main-db: 55 | image: postgres:14-alpine 56 | container_name: "main-db" 57 | ports: 58 | - "6542:5432" 59 | volumes: 60 | - /var/lib/postgresql/main-service 61 | environment: 62 | - POSTGRES_DB=main_db 63 | - POSTGRES_USER=owner 64 | - POSTGRES_PASSWORD=password -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/google-play-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/comment/dto/CommentDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.dto; 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 org.springframework.boot.test.json.JsonContent; 8 | import ru.practicum.category.dto.CategoryDto; 9 | import ru.practicum.event.dto.EventShortDto; 10 | import ru.practicum.user.dto.UserShortDto; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | @JsonTest 17 | public class CommentDtoTest { 18 | 19 | @Autowired 20 | private JacksonTester jacksonTester; 21 | 22 | @Test 23 | void testSerialize() throws Exception { 24 | UserShortDto userShortDto = new UserShortDto(1L, "User"); 25 | CategoryDto categoryDto = new CategoryDto("Meeting new friends", 1L); 26 | EventShortDto eventShortDto = new EventShortDto("11.06 Watching raccoons together in the Central park", 1L, 27 | categoryDto, 0L, LocalDateTime.now().minusDays(1), userShortDto, false, 28 | "Raccoon watching", 0L); 29 | 30 | CommentDto commentDto = new CommentDto("Do they serve lactose-free latte there?", LocalDateTime.now(), 31 | userShortDto, eventShortDto); 32 | 33 | JsonContent commentDtoSaved = jacksonTester.write(commentDto); 34 | 35 | assertThat(commentDtoSaved).hasJsonPath("$.text"); 36 | assertThat(commentDtoSaved).hasJsonPath("$.createdOn"); 37 | assertThat(commentDtoSaved).hasJsonPath("$.user"); 38 | assertThat(commentDtoSaved).hasJsonPath("$.event"); 39 | 40 | assertThat(commentDtoSaved).extractingJsonPathStringValue("$.text").isEqualTo(commentDto.getText()); 41 | assertThat(commentDtoSaved).hasJsonPathValue("$.createdOn"); 42 | assertThat(commentDtoSaved).hasJsonPathValue("$.user"); 43 | assertThat(commentDtoSaved).hasJsonPathValue("$.event"); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /main-service/src/main/resources/templates/fragments/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 38 |
39 | 40 |
41 | 42 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/controller/PublicCompilationController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.ResponseStatus; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import ru.practicum.compilation.dto.CompilationDto; 13 | import ru.practicum.compilation.mapper.CompilationMapper; 14 | import ru.practicum.compilation.service.CompilationService; 15 | 16 | import javax.validation.constraints.Positive; 17 | import javax.validation.constraints.PositiveOrZero; 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping("/compilations") 22 | @RequiredArgsConstructor 23 | @Slf4j 24 | public class PublicCompilationController { 25 | 26 | private final CompilationService compilationService; 27 | private final CompilationMapper compilationMapper; 28 | 29 | @GetMapping 30 | public List getCompilations(@RequestParam(required = false) Boolean pinned, 31 | @PositiveOrZero @RequestParam(defaultValue = "0") Integer from, 32 | @Positive @RequestParam(defaultValue = "10") Integer size) { 33 | 34 | log.info("Calling GET: /compilations with 'pinned': {}, 'from': {}, 'size': {}", pinned, from, size); 35 | return compilationMapper.listCompilationToListCompilationDto(compilationService.getAll(pinned, from, size)); 36 | } 37 | 38 | @GetMapping("/{compId}") 39 | @ResponseStatus(HttpStatus.OK) 40 | public CompilationDto getCompilationById(@PathVariable Long compId) { 41 | 42 | log.info("Calling GET: /compilations/{compId} with 'compId': {}", compId); 43 | return compilationMapper.compilationToCompilationDto(compilationService.get(compId)); 44 | } 45 | } -------------------------------------------------------------------------------- /stats-service/dto/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | ru.practicum 9 | stats-service 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | dto 14 | jar 15 | 16 | 17 | 18 | org.projectlombok 19 | lombok 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-json 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-validation 30 | 31 | 32 | 33 | com.fasterxml.jackson.core 34 | jackson-annotations 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 11 47 | 11 48 | UTF-8 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 3.8.1 58 | 59 | 11 60 | 11 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/security/AuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.security; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.userdetails.User; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.stereotype.Component; 10 | import ru.practicum.exception.UserAuthException; 11 | 12 | @Component 13 | @RequiredArgsConstructor 14 | public class AuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider { 15 | private final UserDetailsService userService; 16 | 17 | UserDetails isUserValid(String username, String password) { 18 | UserDetails user = userService.loadUserByUsername(username); 19 | if (username.equalsIgnoreCase(user.getUsername()) 20 | && password.equals(user.getPassword())) { 21 | UserDetails userDetails = User 22 | .withUsername(username) 23 | .password("NOT_DISCLOSED") 24 | .authorities(((ru.practicum.user.model.User) user).getRoles()) 25 | .build(); 26 | return userDetails; 27 | } 28 | return null; 29 | } 30 | 31 | @Override 32 | public Authentication authenticate(Authentication authentication) { 33 | String username = authentication.getName(); 34 | String password = authentication.getCredentials().toString(); 35 | 36 | UserDetails userDetails = isUserValid(username, password); 37 | 38 | if (userDetails != null) { 39 | return new UsernamePasswordAuthenticationToken( 40 | username, 41 | password, 42 | userDetails.getAuthorities()); 43 | } else { 44 | throw new UserAuthException("Incorrect user credentials!"); 45 | } 46 | } 47 | 48 | @Override 49 | public boolean supports(Class authenticationType) { 50 | return authenticationType 51 | .equals(UsernamePasswordAuthenticationToken.class); 52 | } 53 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.DeleteMapping; 7 | import org.springframework.web.bind.annotation.PatchMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import ru.practicum.category.dto.CategoryDto; 15 | import ru.practicum.category.dto.NewCategoryDto; 16 | import ru.practicum.category.mapper.CategoryMapper; 17 | import ru.practicum.category.service.CategoryService; 18 | 19 | import javax.validation.Valid; 20 | 21 | @RestController 22 | @RequestMapping("/admin/categories") 23 | @RequiredArgsConstructor 24 | @Slf4j 25 | public class AdminCategoryController { 26 | 27 | private final CategoryService categoryService; 28 | private final CategoryMapper categoryMapper; 29 | 30 | @PostMapping 31 | @ResponseStatus(HttpStatus.CREATED) 32 | public CategoryDto add(@RequestBody @Valid NewCategoryDto newCategoryDto) { 33 | 34 | log.info("Calling POST: /admin/categories with 'newCategoryDto':{}", newCategoryDto.toString()); 35 | return categoryMapper.categoryToCategoryDto(categoryService.add(newCategoryDto)); 36 | } 37 | 38 | @PatchMapping("/{catId}") 39 | public CategoryDto update(@PathVariable Long catId, 40 | @RequestBody @Valid NewCategoryDto newCategoryDto) { 41 | 42 | log.info("Calling PATCH: /admin/categories with 'categoryDto': {}", newCategoryDto.toString()); 43 | return categoryMapper.categoryToCategoryDto(categoryService.update(catId, newCategoryDto)); 44 | } 45 | 46 | @DeleteMapping("/{catId}") 47 | @ResponseStatus(HttpStatus.NO_CONTENT) 48 | public void delete(@PathVariable Long catId) { 49 | 50 | log.info("Calling DELETE: /admin/categories/{catId} with 'categoryId': {}", catId); 51 | categoryService.delete(catId); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/web/UserEventController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.web; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.ui.Model; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.ModelAttribute; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.ResponseStatus; 15 | import ru.practicum.category.dto.CategoryDto; 16 | import ru.practicum.category.mapper.CategoryMapper; 17 | import ru.practicum.category.service.CategoryService; 18 | import ru.practicum.event.dto.NewEventDto; 19 | import ru.practicum.event.service.EventService; 20 | import ru.practicum.user.model.User; 21 | import ru.practicum.user.service.UserService; 22 | 23 | import javax.validation.Valid; 24 | import java.util.List; 25 | 26 | @Controller 27 | @RequestMapping("/users/add-event") 28 | @RequiredArgsConstructor 29 | public class UserEventController { 30 | 31 | private final EventService eventService; 32 | private final UserService userService; 33 | private final CategoryService categoryService; 34 | private final CategoryMapper categoryMapper; 35 | 36 | @GetMapping 37 | public String newEventPage(Model model) { 38 | model.addAttribute("event", new NewEventDto()); 39 | 40 | List selectCategoryOptions = categoryMapper.listCategoryToListCategoryDto(categoryService.getAll(0, 10)); 41 | model.addAttribute("selectCategoryOptions", selectCategoryOptions); 42 | 43 | return "add-event"; 44 | } 45 | 46 | @PostMapping 47 | @ResponseStatus(HttpStatus.CREATED) 48 | public String addUserEvent(@ModelAttribute("event") @RequestBody @Valid NewEventDto event) { 49 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 50 | 51 | User currentUser = userService.loadUserByUsername(authentication.getName()); 52 | 53 | eventService.addUserEvent(currentUser.getId(), event); 54 | 55 | return "index"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/comment/mapper/CommentMapperTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.mapper; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import ru.practicum.category.model.Category; 5 | import ru.practicum.comment.dto.CommentDto; 6 | import ru.practicum.comment.dto.NewCommentDto; 7 | import ru.practicum.comment.model.Comment; 8 | import ru.practicum.comment.model.CommentStatus; 9 | import ru.practicum.event.model.Event; 10 | import ru.practicum.event.model.Location; 11 | import ru.practicum.event.model.enums.EventState; 12 | import ru.practicum.user.model.User; 13 | 14 | import java.time.LocalDateTime; 15 | import java.util.List; 16 | 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.assertNotNull; 19 | 20 | public class CommentMapperTest { 21 | 22 | @Test 23 | void newCommentDtoToComment() { 24 | NewCommentDto newCommentDto = new NewCommentDto("Do they serve lactose-free latte there?"); 25 | Comment comment = CommentMapper.INSTANCE.newCommentDtoToComment(newCommentDto); 26 | 27 | assertNotNull(comment); 28 | assertEquals(newCommentDto.getText(), comment.getText()); 29 | } 30 | 31 | @Test 32 | void commentToCommentDto() { 33 | User user = new User(1L, "user@mail.com", "User"); 34 | Category category = new Category(1L, "Meeting new friends"); 35 | Location location = new Location(1, "Central park", 35.6, 22.3); 36 | Event event = new Event(1L, "11.06 Watching raccoons together in the Central park", category, 0L, 37 | LocalDateTime.now().minusDays(10), "Looking for pals to contemplate raccoon existence in the park", 38 | LocalDateTime.now().plusDays(20), user, location, false, 0L, LocalDateTime.now().minusDays(8), 39 | false, EventState.PUBLISHED, "Raccoon watching", 0L, List.of()); 40 | 41 | Comment comment = new Comment(null, "Do they serve lactose-free latte there?", LocalDateTime.now(), 42 | user, CommentStatus.PENDING, event); 43 | 44 | CommentDto commentDto = CommentMapper.INSTANCE.commentToCommentDto(comment); 45 | 46 | assertNotNull(commentDto); 47 | assertEquals(comment.getText(), commentDto.getText()); 48 | assertEquals(comment.getCreatedOn(), commentDto.getCreatedOn()); 49 | assertEquals(comment.getUser().getId(), commentDto.getUser().getId()); 50 | assertEquals(comment.getEvent().getId(), commentDto.getEvent().getId()); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/request/controller/PrivateRequestController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.request.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PatchMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import ru.practicum.request.dto.ParticipationRequestDto; 15 | import ru.practicum.request.mapper.RequestMapper; 16 | import ru.practicum.request.service.RequestService; 17 | 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping("/users/{userId}/requests") 22 | @RequiredArgsConstructor 23 | @Slf4j 24 | public class PrivateRequestController { 25 | 26 | private final RequestService requestService; 27 | private final RequestMapper requestMapper; 28 | 29 | @GetMapping("/requests") 30 | public List getUserRequests(@PathVariable Long userId) { 31 | log.info("Calling GET: /users/{userId}/requests with 'userId': {}", userId); 32 | return requestMapper.listRequestToListRequestDto(requestService.getUserRequests(userId)); 33 | } 34 | 35 | @PostMapping("/requests") 36 | @ResponseStatus(HttpStatus.CREATED) 37 | public ParticipationRequestDto addUserRequest(@PathVariable Long userId, 38 | @RequestParam(name = "eventId") Long eventId) { 39 | log.info("Calling POST: /users/{userId}/requests with 'userId': {}, 'eventId': {}", userId, eventId); 40 | return requestMapper.requestToRequestDto(requestService.addParticipationRequest(userId, eventId)); 41 | } 42 | 43 | @PatchMapping("/requests/{requestId}/cancel") 44 | public ParticipationRequestDto updateUserRequest(@PathVariable Long userId, 45 | @PathVariable Long requestId) { 46 | log.info("Calling PATCH: /users/{userId}/requests/{requestId}/cancel with 'userId': {}, 'requestId': {}", userId, requestId); 47 | return requestMapper.requestToRequestDto(requestService.cancelParticipationRequest(userId, requestId)); 48 | } 49 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/web/AuthController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.web; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.ModelAttribute; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.ResponseStatus; 12 | import ru.practicum.exception.SQLConstraintViolationException; 13 | import ru.practicum.user.dto.NewUserDto; 14 | import ru.practicum.user.dto.UserLoginDto; 15 | import ru.practicum.user.mapper.UserMapper; 16 | import ru.practicum.user.model.User; 17 | import ru.practicum.user.service.UserService; 18 | 19 | import javax.validation.Valid; 20 | 21 | @Controller 22 | @RequestMapping 23 | @RequiredArgsConstructor 24 | public class AuthController { 25 | 26 | private final UserService userService; 27 | private final UserMapper userMapper; 28 | 29 | @GetMapping("/sign-up") 30 | public String getSignUp(Model model) { 31 | model.addAttribute("user", new NewUserDto()); 32 | return "sign-up"; 33 | } 34 | 35 | @PostMapping("/sign-up") 36 | @ResponseStatus(code = HttpStatus.CREATED) 37 | public String createUserAndLogin(@ModelAttribute("user") @Valid NewUserDto user, Model model) { 38 | 39 | if (!user.getPassword().equals(user.getPasswordConfirm())) { 40 | model.addAttribute("errorMessage", "Passwords do not match."); 41 | return "index"; 42 | } 43 | 44 | User userSaved; 45 | 46 | try { 47 | userSaved = userService.add(user); 48 | } catch (SQLConstraintViolationException e) { 49 | model.addAttribute("errorMessage", "User with this name and/or email already exists."); 50 | return "sign-up"; 51 | } 52 | 53 | model.addAttribute("user", userMapper.userToUserDto(userSaved)); 54 | return "index"; 55 | } 56 | 57 | @GetMapping("/sign-in") 58 | public String getLogin(Model model) { 59 | model.addAttribute("user", new NewUserDto()); 60 | return "sign-in"; 61 | } 62 | 63 | @PostMapping("/sign-in") 64 | public String login(@ModelAttribute("user") @Valid UserLoginDto userLoginDto, Model model) { 65 | model.addAttribute("user", userLoginDto); 66 | return "index"; 67 | } 68 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.DeleteMapping; 7 | import org.springframework.web.bind.annotation.PatchMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import ru.practicum.compilation.dto.CompilationDto; 15 | import ru.practicum.compilation.dto.NewCompilationDto; 16 | import ru.practicum.compilation.dto.UpdateCompilationRequest; 17 | import ru.practicum.compilation.mapper.CompilationMapper; 18 | import ru.practicum.compilation.service.CompilationService; 19 | 20 | import javax.validation.Valid; 21 | 22 | @RestController 23 | @RequestMapping("/admin/compilations") 24 | @RequiredArgsConstructor 25 | @Slf4j 26 | public class AdminCompilationController { 27 | 28 | private final CompilationService compilationService; 29 | private final CompilationMapper compilationMapper; 30 | 31 | @PostMapping 32 | @ResponseStatus(HttpStatus.CREATED) 33 | public CompilationDto addEventCompilation(@RequestBody @Valid NewCompilationDto compilationDto) { 34 | 35 | log.info("Calling POST: /admin/compilations with 'compilationDto': {}", compilationDto); 36 | return compilationMapper.compilationToCompilationDto(compilationService.add(compilationDto)); 37 | } 38 | 39 | @PatchMapping("/{compId}") 40 | public CompilationDto updateEventCompilation(@PathVariable Long compId, 41 | @RequestBody @Valid UpdateCompilationRequest compRequest) { 42 | 43 | log.info("Calling PATCH: /admin/compilations/{compId} with 'compId': {}", compId); 44 | return compilationMapper.compilationToCompilationDto(compilationService.update(compId, compRequest)); 45 | } 46 | 47 | @DeleteMapping("/{compId}") 48 | @ResponseStatus(HttpStatus.NO_CONTENT) 49 | public void deleteEventCompilation(@PathVariable Long compId) { 50 | 51 | log.info("Calling DELETE: /admin/compilations/{compId} with 'compId': {}", compId); 52 | compilationService.delete(compId); 53 | } 54 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/test/java/ru/practicum/dto/RequestDtoTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.dto; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 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 org.springframework.boot.test.json.JsonContent; 9 | import ru.practicum.RequestDto; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import static org.junit.jupiter.api.Assertions.assertNotNull; 15 | 16 | @JsonTest 17 | public class RequestDtoTest { 18 | 19 | @Autowired 20 | private JacksonTester jacksonTester; 21 | 22 | private RequestDto requestDto; 23 | 24 | @BeforeEach 25 | void setup() { 26 | requestDto = new RequestDto(1, "ewm-main-service", "/events/1", "192.163.0.1", LocalDateTime.now()); 27 | } 28 | 29 | @Test 30 | void serialize() throws Exception { 31 | JsonContent requestDtoSaved = jacksonTester.write(requestDto); 32 | 33 | assertThat(requestDtoSaved).hasJsonPath("$.id"); 34 | assertThat(requestDtoSaved).hasJsonPath("$.app"); 35 | assertThat(requestDtoSaved).hasJsonPath("$.uri"); 36 | assertThat(requestDtoSaved).hasJsonPath("$.ip"); 37 | assertThat(requestDtoSaved).hasJsonPath("$.timestamp"); 38 | 39 | assertThat(requestDtoSaved).extractingJsonPathNumberValue("$.id").isEqualTo(requestDto.getId()); 40 | assertThat(requestDtoSaved).extractingJsonPathStringValue("$.app").isEqualTo(requestDto.getApp()); 41 | assertThat(requestDtoSaved).extractingJsonPathStringValue("$.uri").isEqualTo(requestDto.getUri()); 42 | assertThat(requestDtoSaved).extractingJsonPathStringValue("$.ip").isEqualTo(requestDto.getIp()); 43 | 44 | assertThat(requestDtoSaved).hasJsonPathValue("$.timestamp"); 45 | } 46 | 47 | @Test 48 | void deserialize() throws Exception { 49 | String json = "{\"app\":\"main-service\"," + 50 | "\"uri\":\"/events/1\"," + 51 | "\"ip\":\"192.163.0.1\"}"; 52 | 53 | RequestDto requestDto = jacksonTester.parseObject(json); 54 | 55 | assertNotNull(requestDto); 56 | 57 | assertThat(requestDto.getApp()).isEqualTo(requestDto.getApp()); 58 | assertThat(requestDto.getUri()).isEqualTo(requestDto.getUri()); 59 | assertThat(requestDto.getIp()).isEqualTo(requestDto.getIp()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/controller/AdminUserController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.web.bind.annotation.DeleteMapping; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.ResponseStatus; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import ru.practicum.user.dto.NewUserDto; 16 | import ru.practicum.user.dto.UserDto; 17 | import ru.practicum.user.mapper.UserMapper; 18 | import ru.practicum.user.model.User; 19 | import ru.practicum.user.service.UserService; 20 | 21 | import javax.validation.Valid; 22 | import java.util.List; 23 | 24 | @RestController 25 | @RequestMapping("/admin/users") 26 | @RequiredArgsConstructor 27 | @Slf4j 28 | public class AdminUserController { 29 | 30 | private final UserService userService; 31 | private final UserMapper userMapper; 32 | 33 | @PostMapping 34 | @ResponseStatus(HttpStatus.CREATED) 35 | public UserDto addAdminUser(@RequestBody @Valid NewUserDto newUserDto) { 36 | log.info("Calling POST: /admin/users with 'newUserRequestDto': {}", newUserDto.toString()); 37 | User user = userService.addAdminUser(userMapper.newUserRequestDtoToUser(newUserDto)); 38 | return userMapper.userToUserDto(user); 39 | } 40 | 41 | @GetMapping 42 | public List getAdminUsers(@RequestParam(name = "ids", required = false) List ids, 43 | @RequestParam(name = "from", defaultValue = "0", required = false) Integer from, 44 | @RequestParam(name = "size", defaultValue = "10", required = false) Integer size) { 45 | 46 | log.info("Calling GET: /admin/users with 'ids': {}, 'from': {}, 'size': {}", ids, from, size); 47 | return userMapper.listUserToListUserDto(userService.getByIds(ids, from, size)); 48 | } 49 | 50 | @DeleteMapping("/{userId}") 51 | @ResponseStatus(HttpStatus.NO_CONTENT) 52 | public void deleteAdminUser(@PathVariable Long userId) { 53 | log.info("Calling DELETE: /admin/users/{userId} with 'userId': {}", userId); 54 | userService.delete(userId); 55 | } 56 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/model/User.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | 12 | import javax.persistence.Entity; 13 | import javax.persistence.FetchType; 14 | import javax.persistence.GeneratedValue; 15 | import javax.persistence.GenerationType; 16 | import javax.persistence.Id; 17 | import javax.persistence.JoinColumn; 18 | import javax.persistence.JoinTable; 19 | import javax.persistence.ManyToMany; 20 | import javax.persistence.Table; 21 | import javax.persistence.Transient; 22 | import javax.persistence.UniqueConstraint; 23 | import javax.validation.constraints.Size; 24 | import java.util.Collection; 25 | import java.util.Set; 26 | 27 | @Entity 28 | @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "email"})}) 29 | @Getter 30 | @Setter 31 | @ToString 32 | @EqualsAndHashCode(of = "id") 33 | @NoArgsConstructor 34 | @AllArgsConstructor 35 | public class User implements UserDetails { 36 | 37 | @Id 38 | @GeneratedValue(strategy = GenerationType.IDENTITY) 39 | private Long id; 40 | 41 | private String email; 42 | 43 | @Size(min = 1, max = 255) 44 | private String name; 45 | 46 | @Size(min = 5) 47 | private String username; 48 | 49 | @Size(min = 5) 50 | private String password; 51 | 52 | @Transient 53 | private String passwordConfirm; 54 | 55 | @ManyToMany(fetch = FetchType.EAGER) 56 | @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) 57 | private Set roles; 58 | 59 | public User(Long id, String email, String name) { 60 | this.id = id; 61 | this.email = email; 62 | this.name = name; 63 | } 64 | 65 | public Set getRole() { 66 | return roles; 67 | } 68 | 69 | @Override 70 | public Collection getAuthorities() { 71 | return getRoles(); 72 | } 73 | 74 | @Override 75 | public boolean isAccountNonExpired() { 76 | return true; 77 | } 78 | 79 | @Override 80 | public boolean isAccountNonLocked() { 81 | return true; 82 | } 83 | 84 | @Override 85 | public boolean isCredentialsNonExpired() { 86 | return true; 87 | } 88 | 89 | @Override 90 | public boolean isEnabled() { 91 | return true; 92 | } 93 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/service/StatsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import ru.practicum.RequestDto; 6 | import ru.practicum.RequestOutputDto; 7 | import ru.practicum.mapper.RequestMapper; 8 | import ru.practicum.model.App; 9 | import ru.practicum.model.Request; 10 | import ru.practicum.repository.AppRepository; 11 | import ru.practicum.repository.RequestRepository; 12 | 13 | import java.time.LocalDateTime; 14 | import java.util.List; 15 | import java.util.Optional; 16 | 17 | @Service 18 | @RequiredArgsConstructor 19 | public class StatsServiceImpl implements StatsService { 20 | 21 | private final RequestRepository requestRepository; 22 | private final AppRepository appRepository; 23 | private final RequestMapper mapper; 24 | 25 | public void addRequest(RequestDto requestDto) { 26 | Optional optionalApp = appRepository.findByName(requestDto.getApp()); 27 | 28 | App app = optionalApp.orElseGet(() -> appRepository.save(new App(requestDto.getApp()))); 29 | 30 | Request request = mapper.requestDtoToRequest(requestDto); 31 | request.setApp(app); 32 | requestRepository.save(request); 33 | } 34 | 35 | public List getRequestsWithViews(LocalDateTime start, LocalDateTime end, List uris, Boolean unique) { 36 | 37 | if (unique) { 38 | if (uris == null || uris.isEmpty()) { 39 | return requestRepository.getUniqueIpRequestsWithoutUri(start, end); 40 | } 41 | return requestRepository.getUniqueIpRequestsWithUri(start, end, uris); 42 | } else { 43 | if (uris == null || uris.isEmpty()) { 44 | return requestRepository.getAllRequestsWithoutUri(start, end); 45 | } 46 | return requestRepository.getAllRequestsWithUri(start, end, uris); 47 | } 48 | } 49 | 50 | @Override 51 | public List getRequestsWithViewsByIp(LocalDateTime start, LocalDateTime end, List uris, Boolean unique, String ip) { 52 | 53 | if (unique) { 54 | if (uris == null || uris.isEmpty()) { 55 | return requestRepository.getUniqueIpRequestsWithoutUriByIp(start, end, ip); 56 | } 57 | return requestRepository.getUniqueIpRequestsWithUriByIp(start, end, uris, ip); 58 | } else { 59 | if (uris == null || uris.isEmpty()) { 60 | return requestRepository.getAllRequestsWithoutUriByIp(start, end, ip); 61 | } 62 | return requestRepository.getAllRequestsWithUriByIp(start, end, uris, ip); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/css/sign-up-in-styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,600&display=swap'); 2 | 3 | * { 4 | top: 0; 5 | left: 0; 6 | padding: 0; 7 | margin: 0; 8 | border: none; 9 | box-sizing: border-box; 10 | font-family: "Montserrat", sans-serif; 11 | color: var(--font-color); 12 | } 13 | 14 | :root { 15 | --accent-color: #d24d32; 16 | --secondary-accent-color: #14705e; 17 | --accent-background-color: #DCDCDB; 18 | --font-color: #1f2328; 19 | --form-background: #F5F7F8; 20 | } 21 | 22 | body { 23 | width: 100%; 24 | min-height: 100vh; 25 | background-color: #FFFFFF; 26 | } 27 | 28 | /*---------------SIGN-UP FORM-------------*/ 29 | 30 | .main { 31 | display: flex; 32 | flex-direction: column; 33 | height: 100vh; 34 | } 35 | 36 | .pop-up-form { 37 | margin: auto; 38 | width: 450px; 39 | background: #FFF; 40 | border: var(--accent-color) 2px solid; 41 | border-radius: 20px; 42 | padding: 40px 35px; 43 | } 44 | 45 | .pop-up-header { 46 | font-size: 28px; 47 | font-weight: 700; 48 | color: var(--font-color); 49 | text-align: center; 50 | margin: 25px 0 30px; 51 | } 52 | 53 | .pop-up-header a { 54 | text-decoration: underline var(--accent-color); 55 | } 56 | 57 | .pop-up-fields { 58 | display: flex; 59 | flex-direction: column; 60 | align-items: center; 61 | /*margin: 30px auto 20px;*/ 62 | margin: 0 auto; 63 | max-width: 350px; 64 | /*width: fit-content;*/ 65 | } 66 | 67 | .pop-up-input { 68 | width: 100%; 69 | padding: 12px 20px; 70 | font-size: 16px; 71 | font-weight: 600; 72 | background: var(--form-background); 73 | border: var(--accent-background-color) 2px solid; 74 | border-radius: 10px; 75 | } 76 | 77 | .pop-up-input:not(:last-of-type) { 78 | margin-bottom: 20px; 79 | } 80 | 81 | .pop-up-input::placeholder { 82 | font-weight: 500; 83 | opacity: 0.5; 84 | } 85 | 86 | .pop-up-input:focus { 87 | outline: none; 88 | border: var(--accent-color) 2px solid; 89 | border-radius: 10px; 90 | } 91 | 92 | .pop-up-btn { 93 | margin-top: 40px; 94 | background: var(--accent-color); 95 | padding: 10px 30px; 96 | font-size: 16px; 97 | font-weight: 600; 98 | color: #FFF; 99 | text-decoration: none; 100 | border-radius: 10px; 101 | cursor: pointer; 102 | } 103 | 104 | .pop-up-btn:hover { 105 | background: var(--secondary-accent-color); 106 | /*color: var(--font-color);*/ 107 | } 108 | 109 | .x-close-btn { 110 | position: relative; 111 | left: 98%; 112 | top: -100%; 113 | content: url(../images/x-close-btn.png); 114 | width: 24px; 115 | cursor: pointer; 116 | } 117 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/controller/PublicEventController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.format.annotation.DateTimeFormat; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import ru.practicum.event.dto.EventFullDto; 12 | import ru.practicum.event.dto.EventShortDto; 13 | import ru.practicum.event.mapper.EventMapper; 14 | import ru.practicum.event.model.enums.EventSort; 15 | import ru.practicum.event.service.EventService; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequestMapping("/events") 23 | @RequiredArgsConstructor 24 | @Slf4j 25 | public class PublicEventController { 26 | 27 | private final EventService eventService; 28 | private final EventMapper eventMapper; 29 | 30 | @GetMapping 31 | public List getAll( 32 | @RequestParam(required = false) String text, 33 | @RequestParam(required = false) List categories, 34 | @RequestParam(required = false) Boolean paid, 35 | @RequestParam(required = false) 36 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart, 37 | @RequestParam(required = false) 38 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd, 39 | @RequestParam(defaultValue = "false", required = false) Boolean onlyAvailable, 40 | @RequestParam(required = false, defaultValue = "EVENT_DATE") EventSort sort, 41 | @RequestParam(defaultValue = "0", required = false) Integer from, 42 | @RequestParam(defaultValue = "10", required = false) Integer size, 43 | HttpServletRequest request) { 44 | 45 | log.info("Calling GET: /events with 'text': {}, 'categories': {}, 'paid': {}, 'rangeStart': {}," + 46 | " 'rangeEnd': {}, 'onlyAvailable': {}, 'sort': {}, 'from': {}, 'size': {}", 47 | text, categories, paid, rangeStart, rangeEnd, onlyAvailable, sort, from, size); 48 | 49 | return eventMapper.listEventToListEventShortDto( 50 | eventService.getAll(text, categories, paid, rangeStart, rangeEnd, onlyAvailable, from, size, sort, request)); 51 | } 52 | 53 | @GetMapping("/{id}") 54 | public EventFullDto getById(@PathVariable Long id, HttpServletRequest request) { 55 | log.info("Calling GET: /events/{id} with 'id': {}", id); 56 | return eventMapper.eventToEventFullDto(eventService.get(id, request)); 57 | } 58 | } -------------------------------------------------------------------------------- /stats-service/client/src/main/java/ru/practicum/StatsClient.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | 7 | import java.util.List; 8 | 9 | public class StatsClient { 10 | private final WebClient webClient; 11 | 12 | // public StatsClient(@Value("${stats.server.url}") String statsServerUrl) { 13 | public StatsClient(@Value("http://localhost/:9090") String statsServerUrl) { 14 | webClient = WebClient.create(statsServerUrl); 15 | } 16 | 17 | public void addRequest(RequestDto requestDto) { 18 | webClient.post().uri("/hit").bodyValue(requestDto).retrieve().bodyToMono(Object.class).block(); 19 | } 20 | 21 | public ResponseEntity> getStats(String start, 22 | String end, 23 | List uris, 24 | Boolean unique) { 25 | 26 | ResponseEntity> listResponseEntity = webClient.get() 27 | .uri(uriBuilder -> { 28 | uriBuilder.path("/stats") 29 | .queryParam("start", start) 30 | .queryParam("end", end); 31 | if (uris != null) 32 | uriBuilder.queryParam("uris", String.join(",", uris)); 33 | if (unique != null) 34 | uriBuilder.queryParam("unique", unique); 35 | return uriBuilder.build(); 36 | }) 37 | .retrieve() 38 | .toEntityList(RequestOutputDto.class) 39 | .block(); 40 | return listResponseEntity; 41 | } 42 | 43 | public ResponseEntity> getStatsByIp(String start, 44 | String end, 45 | List uris, 46 | Boolean unique, 47 | String ip) { 48 | 49 | ResponseEntity> listResponseEntity = webClient.get() 50 | .uri(uriBuilder -> { 51 | uriBuilder.path("/statsByIp") 52 | .queryParam("start", start) 53 | .queryParam("end", end) 54 | .queryParam("ip", ip); 55 | if (uris != null) 56 | uriBuilder.queryParam("uris", String.join(",", uris)); 57 | if (unique != null) 58 | uriBuilder.queryParam("unique", unique); 59 | return uriBuilder.build(); 60 | }) 61 | .retrieve() 62 | .toEntityList(RequestOutputDto.class) 63 | .block(); 64 | return listResponseEntity; 65 | } 66 | } -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/controller/AdminCommentController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.format.annotation.DateTimeFormat; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PatchMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import ru.practicum.comment.dto.CommentDto; 13 | import ru.practicum.comment.dto.CommentStatusUpdateRequest; 14 | import ru.practicum.comment.mapper.CommentMapper; 15 | import ru.practicum.comment.model.CommentStatus; 16 | import ru.practicum.comment.service.CommentService; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequestMapping("/admin/comments") 23 | @RequiredArgsConstructor 24 | @Slf4j 25 | public class AdminCommentController { 26 | 27 | private final CommentService commentService; 28 | private final CommentMapper commentMapper; 29 | 30 | @GetMapping 31 | public List getAdminComments(@RequestParam(name = "text", required = false) String text, 32 | @RequestParam(name = "users", required = false) List users, 33 | @RequestParam(name = "statuses", required = false) List statuses, 34 | @RequestParam(name = "events", required = false) List events, 35 | @RequestParam(name = "rangeStart", required = false) 36 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart, 37 | @RequestParam(name = "rangeEnd", required = false) 38 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd, 39 | @RequestParam(name = "from", defaultValue = "0") Integer from, 40 | @RequestParam(name = "size", defaultValue = "10") Integer size) { 41 | 42 | log.info("Calling GET: /admin/comments with 'test': {}, 'users': {}, 'statuses': {}, 'events': {}, 'rangeStart': {}, " + 43 | "'rangeEnd': {}, 'from': {}, 'size': {}", text, users, statuses, events, rangeStart, rangeEnd, from, size); 44 | return commentMapper.listCommentToListCommentDto( 45 | commentService.getAdminComments(text, users, statuses, events, rangeStart, rangeEnd, from, size)); 46 | } 47 | 48 | @PatchMapping 49 | public List moderateAdminComments(@RequestBody CommentStatusUpdateRequest updateRequest) { 50 | log.info("Calling PATCH: /admin/comments with 'updateRequest': {}", updateRequest); 51 | return commentMapper.listCommentToListCommentDto(commentService.moderateAdminComments(updateRequest)); 52 | } 53 | } -------------------------------------------------------------------------------- /main-service/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS roles, users, user_roles, categories, locations, events, compilations, comp_events, requests, comments CASCADE; 2 | 3 | CREATE TABLE IF NOT EXISTS roles ( 4 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, 5 | name VARCHAR UNIQUE NOT NULL 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS users ( 9 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY NOT NULL, 10 | email VARCHAR UNIQUE NOT NULL, 11 | name VARCHAR UNIQUE NOT NULL, 12 | username VARCHAR UNIQUE NOT NULL, 13 | password VARCHAR NOT NULL 14 | ); 15 | 16 | CREATE TABLE IF NOT EXISTS user_roles ( 17 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 18 | user_id BIGINT NOT NULL REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, 19 | role_id BIGINT NOT NULL REFERENCES roles(id) ON UPDATE CASCADE ON DELETE CASCADE 20 | ); 21 | 22 | CREATE TABLE IF NOT EXISTS categories ( 23 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 24 | name VARCHAR UNIQUE NOT NULL 25 | ); 26 | 27 | CREATE TABLE IF NOT EXISTS locations ( 28 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 29 | description VARCHAR, 30 | lat DOUBLE PRECISION NOT NULL, 31 | lon DOUBLE PRECISION NOT NULL 32 | ); 33 | 34 | CREATE TABLE IF NOT EXISTS events ( 35 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 36 | annotation VARCHAR NOT NULL, 37 | cat_id BIGINT NOT NULL REFERENCES categories(id) ON UPDATE CASCADE ON DELETE CASCADE, 38 | confirmed_requests INTEGER, 39 | created_on TIMESTAMP, 40 | description VARCHAR, 41 | event_date TIMESTAMP NOT NULL, 42 | initiator BIGINT NOT NULL REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, 43 | location BIGINT NOT NULL REFERENCES locations(id) ON UPDATE CASCADE ON DELETE CASCADE, 44 | paid BOOLEAN NOT NULL, 45 | participant_limit INTEGER DEFAULT 0, 46 | published_on TIMESTAMP WITHOUT TIME ZONE, 47 | request_moderation BOOLEAN DEFAULT TRUE, 48 | state VARCHAR, 49 | title VARCHAR NOT NULL, 50 | views BIGINT 51 | ); 52 | 53 | CREATE TABLE IF NOT EXISTS compilations ( 54 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 55 | pinned BOOLEAN, 56 | title VARCHAR 57 | ); 58 | 59 | CREATE TABLE IF NOT EXISTS comp_events ( 60 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 61 | compilation_id BIGINT NOT NULL REFERENCES compilations(id) ON UPDATE CASCADE ON DELETE CASCADE, 62 | event_id BIGINT NOT NULL REFERENCES events(id) ON UPDATE CASCADE ON DELETE CASCADE 63 | ); 64 | 65 | CREATE TABLE IF NOT EXISTS requests ( 66 | id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 67 | requester_id BIGINT NOT NULL REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, 68 | event_id BIGINT NOT NULL REFERENCES events(id) ON UPDATE CASCADE ON DELETE CASCADE, 69 | created TIMESTAMP NOT NULL, 70 | status VARCHAR NOT NULL 71 | ); 72 | 73 | CREATE TABLE IF NOT EXISTS comments ( 74 | id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 75 | event_id BIGINT NOT NULL REFERENCES events(id), 76 | user_id BIGINT NOT NULL REFERENCES users(id), 77 | text VARCHAR NOT NULL, 78 | status VARCHAR NOT NULL, 79 | created TIMESTAMP NOT NULL 80 | ); -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/controller/AdminEventController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.format.annotation.DateTimeFormat; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PatchMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import ru.practicum.event.dto.EventFullDto; 14 | import ru.practicum.event.dto.EventUpdateDto; 15 | import ru.practicum.event.mapper.EventMapper; 16 | import ru.practicum.event.model.enums.EventState; 17 | import ru.practicum.event.service.EventService; 18 | 19 | import javax.validation.Valid; 20 | import java.time.LocalDateTime; 21 | import java.util.List; 22 | 23 | @RestController 24 | @RequestMapping("/admin/events") 25 | @RequiredArgsConstructor 26 | @Slf4j 27 | public class AdminEventController { 28 | 29 | private final EventService eventService; 30 | private final EventMapper eventMapper; 31 | 32 | @GetMapping 33 | public List getAdminEvents(@RequestParam(name = "users", required = false) List users, 34 | @RequestParam(name = "states", required = false) List states, 35 | @RequestParam(name = "categories", required = false) List categories, 36 | @RequestParam(name = "rangeStart", required = false) 37 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart, 38 | @RequestParam(name = "rangeEnd", required = false) 39 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd, 40 | @RequestParam(name = "from", defaultValue = "0") Integer from, 41 | @RequestParam(name = "size", defaultValue = "10") Integer size) { 42 | 43 | log.info("Calling GET: /admin/events with 'users': {}, 'states': {}, 'categories': {}, 'rangeStart': {}, " + 44 | "'rangeEnd': {}, 'from': {}, 'size': {}", users, states, categories, rangeStart, rangeEnd, from, size); 45 | 46 | return eventMapper.listEventToListEventFullDto(eventService.getAdminEvents(users, states, categories, rangeStart, rangeEnd, from, size)); 47 | } 48 | 49 | @PatchMapping(path = "/{eventId}") 50 | public EventFullDto updateAdminEvent(@PathVariable("eventId") Long eventId, 51 | @RequestBody @Valid EventUpdateDto eventUpdateDto) { 52 | log.info("Calling PATCH: /admin/events/{eventId} with 'eventId': {}, 'eventAdminPatchDto': {}", eventId, eventUpdateDto); 53 | return eventMapper.eventToEventFullDto(eventService.updateAdminEvent(eventId, eventUpdateDto)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/category/controller/PublicCategoryControllerTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.test.context.TestPropertySource; 12 | import org.springframework.test.web.servlet.MockMvc; 13 | import ru.practicum.category.dto.CategoryDto; 14 | import ru.practicum.category.dto.NewCategoryDto; 15 | import ru.practicum.category.service.CategoryService; 16 | 17 | import java.util.List; 18 | 19 | import static org.hamcrest.Matchers.is; 20 | import static org.mockito.Mockito.*; 21 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 22 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 23 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 24 | 25 | @WebMvcTest(PublicCategoryController.class) 26 | @AutoConfigureMockMvc 27 | @TestPropertySource(properties = {"db.name=test"}) 28 | public class PublicCategoryControllerTest { 29 | 30 | @Autowired 31 | private ObjectMapper mapper; 32 | 33 | @MockBean 34 | private CategoryService categoryService; 35 | 36 | @Autowired 37 | private MockMvc mockMvc; 38 | 39 | private CategoryDto categoryDto; 40 | private NewCategoryDto newCategoryDto; 41 | 42 | @BeforeEach 43 | void setup() { 44 | newCategoryDto = new NewCategoryDto("Meeting new friends"); 45 | categoryDto = new CategoryDto("Meeting new friends", 1L); 46 | } 47 | 48 | @Test 49 | void get_shouldReturnStatusOk() throws Exception { 50 | List categoryDtoList = List.of(categoryDto); 51 | 52 | when(categoryService.getAll(any(), any())).thenReturn(categoryDtoList); 53 | 54 | mockMvc.perform(get("/categories") 55 | .param("from", String.valueOf(0)) 56 | .param("size", String.valueOf(10)) 57 | .accept(MediaType.APPLICATION_JSON)) 58 | .andExpect(status().isOk()) 59 | .andExpect(jsonPath("$.[0].id", is(categoryDto.getId().intValue()))) 60 | .andExpect(jsonPath("$.[0].name", is(categoryDto.getName()))); 61 | 62 | verify(categoryService, times(1)).getAll(any(), any()); 63 | } 64 | 65 | @Test 66 | void getById_shouldReturnStatusOk() throws Exception { 67 | when(categoryService.get(any())).thenReturn(categoryDto); 68 | 69 | mockMvc.perform(get("/categories/{categoryId}", 1) 70 | .accept(MediaType.APPLICATION_JSON)) 71 | .andExpect(status().isOk()) 72 | .andExpect(jsonPath("$.id", is(categoryDto.getId().intValue()))) 73 | .andExpect(jsonPath("$.name", is(categoryDto.getName()))); 74 | 75 | verify(categoryService, times(1)).get(any()); 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /main-service/src/main/resources/templates/fragments/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 72 | 73 |
74 | 75 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/images/app-store-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/comment/repository/CommentSpecRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.comment.repository; 2 | 3 | import org.springframework.data.jpa.domain.Specification; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | import org.springframework.stereotype.Repository; 7 | import ru.practicum.comment.model.Comment; 8 | import ru.practicum.comment.model.CommentStatus; 9 | 10 | import javax.persistence.criteria.CriteriaBuilder; 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | 14 | @Repository 15 | public interface CommentSpecRepository extends JpaRepository, JpaSpecificationExecutor { 16 | 17 | static Specification hasText(String text) { 18 | return (event, query, critBuilder) -> { 19 | if (text == null) { 20 | return critBuilder.isTrue(critBuilder.literal(true)); 21 | } else { 22 | return critBuilder.like(critBuilder.lower(event.get("text")), "%" + text.toLowerCase() + "%"); 23 | } 24 | }; 25 | } 26 | 27 | static Specification hasEvents(List events) { 28 | return (event, query, critBuilder) -> { 29 | if (events == null || events.size() == 0) { 30 | return critBuilder.isTrue(critBuilder.literal(true)); 31 | } else { 32 | CriteriaBuilder.In eventIds = critBuilder.in(event.get("event")); 33 | for (long eventId : events) { 34 | eventIds.value(eventId); 35 | } 36 | return eventIds; 37 | } 38 | }; 39 | } 40 | 41 | static Specification hasUsers(List users) { 42 | return (event, query, critBuilder) -> { 43 | if (users == null || users.size() == 0) { 44 | return critBuilder.isTrue(critBuilder.literal(true)); 45 | } else { 46 | CriteriaBuilder.In userIds = critBuilder.in(event.get("commenter")); 47 | for (long userId : users) { 48 | userIds.value(userId); 49 | } 50 | return userIds; 51 | } 52 | }; 53 | } 54 | 55 | static Specification hasStatuses(List statuses) { 56 | return (event, query, critBuilder) -> { 57 | if (statuses == null || statuses.size() == 0) { 58 | return critBuilder.isTrue(critBuilder.literal(true)); 59 | } else { 60 | CriteriaBuilder.In commentStatuses = critBuilder.in(event.get("status")); 61 | for (CommentStatus status : statuses) { 62 | commentStatuses.value(status); 63 | } 64 | return commentStatuses; 65 | } 66 | }; 67 | } 68 | 69 | static Specification hasRangeStart(LocalDateTime rangeStart) { 70 | return (event, query, critBuilder) -> { 71 | if (rangeStart == null) { 72 | return critBuilder.isTrue(critBuilder.literal(true)); 73 | } else { 74 | return critBuilder.greaterThan(event.get("createdOn"), rangeStart); 75 | } 76 | }; 77 | } 78 | 79 | static Specification hasRangeEnd(LocalDateTime rangeEnd) { 80 | return (event, query, critBuilder) -> { 81 | if (rangeEnd == null) { 82 | return critBuilder.isTrue(critBuilder.literal(true)); 83 | } else { 84 | return critBuilder.lessThan(event.get("createdOn"), rangeEnd); 85 | } 86 | }; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /main-service/src/test/java/ru/practicum/category/controller/AdminCategoryControllerTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 9 | import org.springframework.boot.test.mock.mockito.MockBean; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.test.context.TestPropertySource; 12 | import org.springframework.test.web.servlet.MockMvc; 13 | import ru.practicum.category.dto.CategoryDto; 14 | import ru.practicum.category.dto.NewCategoryDto; 15 | import ru.practicum.category.service.CategoryService; 16 | 17 | import static org.hamcrest.Matchers.is; 18 | import static org.mockito.Mockito.*; 19 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 21 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 22 | 23 | @WebMvcTest(AdminCategoryController.class) 24 | @AutoConfigureMockMvc 25 | @TestPropertySource(properties = {"db.name=test"}) 26 | public class AdminCategoryControllerTest { 27 | 28 | @Autowired 29 | private ObjectMapper mapper; 30 | 31 | @MockBean 32 | private CategoryService categoryService; 33 | 34 | @Autowired 35 | private MockMvc mockMvc; 36 | 37 | private NewCategoryDto newCategoryDto; 38 | private CategoryDto categoryDto; 39 | 40 | @BeforeEach 41 | void setup() { 42 | newCategoryDto = new CategoryDto("Meeting new friends.", 1L); 43 | categoryDto = new CategoryDto("Meeting new friends.", 1L); 44 | } 45 | 46 | @Test 47 | void add_shouldReturnCategoryDtoAndStatusCreated() throws Exception { 48 | when(categoryService.add(any())).thenReturn(categoryDto); 49 | 50 | mockMvc.perform(post("/admin/categories") 51 | .content(mapper.writeValueAsString(newCategoryDto)) 52 | .contentType(MediaType.APPLICATION_JSON) 53 | .accept(MediaType.APPLICATION_JSON)) 54 | .andExpect(status().isCreated()) 55 | .andExpect(jsonPath("$.id", is(categoryDto.getId()), Long.class)) 56 | .andExpect(jsonPath("$.name", is(categoryDto.getName()))); 57 | 58 | verify(categoryService, times(1)).add(any()); 59 | } 60 | 61 | @Test 62 | void update_shouldReturnCategoryDto() throws Exception { 63 | when(categoryService.update(any(), any())).thenReturn(categoryDto); 64 | 65 | mockMvc.perform(patch("/admin/categories/{catId}", 1) 66 | .content(mapper.writeValueAsString(newCategoryDto)) 67 | .contentType(MediaType.APPLICATION_JSON) 68 | .accept(MediaType.APPLICATION_JSON)) 69 | .andExpect(status().isOk()) 70 | .andExpect(jsonPath("$.id", is(categoryDto.getId()), Long.class)) 71 | .andExpect(jsonPath("$.name", is(categoryDto.getName()))); 72 | 73 | verify(categoryService, times(1)).update(any(), any()); 74 | } 75 | 76 | @Test 77 | void delete_shouldReturnStatusNoContent() throws Exception { 78 | doNothing().when(categoryService).delete(any()); 79 | 80 | mockMvc.perform(delete("/admin/categories/{catId}", 1)) 81 | .andExpect(status().isNoContent()); 82 | 83 | verify(categoryService, times(1)).delete(any()); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.category.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.dao.DataIntegrityViolationException; 5 | import org.springframework.dao.EmptyResultDataAccessException; 6 | import org.springframework.data.domain.PageRequest; 7 | import org.springframework.data.domain.Pageable; 8 | import org.springframework.data.domain.Sort; 9 | import org.springframework.stereotype.Service; 10 | import ru.practicum.category.dto.NewCategoryDto; 11 | import ru.practicum.category.mapper.CategoryMapper; 12 | import ru.practicum.category.model.Category; 13 | import ru.practicum.category.repository.CategoryRepository; 14 | import ru.practicum.event.repository.EventRepository; 15 | import ru.practicum.exception.ObjectNotFoundException; 16 | import ru.practicum.exception.RequestConflictException; 17 | import ru.practicum.exception.SQLConstraintViolationException; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | @Service 24 | @RequiredArgsConstructor 25 | public class CategoryServiceImpl implements CategoryService { 26 | 27 | private final CategoryRepository categoryRepository; 28 | private final EventRepository eventRepository; 29 | private final CategoryMapper categoryMapper; 30 | 31 | @Override 32 | public Category add(NewCategoryDto newCategoryDto) { 33 | 34 | Category category = categoryMapper.newCategoryDtoToCategory(newCategoryDto); 35 | 36 | try { 37 | category = categoryRepository.save(category); 38 | } catch (DataIntegrityViolationException e) { 39 | throw new SQLConstraintViolationException("Category with name = " + newCategoryDto.getName() + " already exists."); 40 | } 41 | 42 | return category; 43 | } 44 | 45 | @Override 46 | public Category update(Long catId, NewCategoryDto categoryDto) { 47 | 48 | Category category = categoryRepository.findById(catId).orElseThrow(() -> { 49 | throw new ObjectNotFoundException("Category with id = " + catId + " doesn't exist."); 50 | }); 51 | 52 | category.setName(categoryDto.getName()); 53 | 54 | try { 55 | category = categoryRepository.save(category); 56 | } catch (DataIntegrityViolationException e) { 57 | throw new SQLConstraintViolationException("Category with name = " + categoryDto.getName() + " already exists."); 58 | } 59 | 60 | return category; 61 | } 62 | 63 | @Override 64 | public Category get(Long catId) { 65 | 66 | return categoryRepository.findById(catId).orElseThrow(() -> { 67 | throw new ObjectNotFoundException("Category with id = " + catId + " doesn't exist."); 68 | }); 69 | } 70 | 71 | @Override 72 | public void delete(Long catId) { 73 | 74 | if (eventRepository.existsByCategoryId(catId)) { 75 | throw new RequestConflictException("Failed to delete category. It must not be assigned to any event."); 76 | } 77 | 78 | try { 79 | categoryRepository.deleteById(catId); 80 | } catch (EmptyResultDataAccessException e) { 81 | throw new ObjectNotFoundException("Category with id = " + catId + " doesn't exist."); 82 | } 83 | } 84 | 85 | @Override 86 | public List getAll(Integer from, Integer size) { 87 | 88 | Sort sort = Sort.by("id").ascending(); 89 | Pageable pageable = PageRequest.of(from / size, size, sort); 90 | 91 | List categories = categoryRepository.findAll(pageable).stream() 92 | .collect(Collectors.toList()); 93 | 94 | return (!categories.isEmpty()) ? categories : new ArrayList<>(); 95 | } 96 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/test/java/ru/practicum/controller/StatsControllerTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.mockito.Spy; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 9 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 10 | import org.springframework.boot.test.mock.mockito.MockBean; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.test.context.TestPropertySource; 13 | import org.springframework.test.web.servlet.MockMvc; 14 | import ru.practicum.RequestDto; 15 | import ru.practicum.RequestOutputDto; 16 | import ru.practicum.mapper.RequestMapperImpl; 17 | import ru.practicum.service.StatsServiceImpl; 18 | 19 | import java.time.LocalDateTime; 20 | import java.time.format.DateTimeFormatter; 21 | import java.util.List; 22 | 23 | import static org.hamcrest.Matchers.is; 24 | import static org.mockito.ArgumentMatchers.any; 25 | import static org.mockito.Mockito.*; 26 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 30 | 31 | @WebMvcTest(StatsController.class) 32 | @AutoConfigureMockMvc 33 | @TestPropertySource(properties = {"db.name=test"}) 34 | public class StatsControllerTest { 35 | 36 | @Autowired 37 | private ObjectMapper objectMapper; 38 | 39 | @Spy 40 | private RequestMapperImpl requestMapper; 41 | 42 | @MockBean 43 | private StatsServiceImpl statsService; 44 | 45 | @Autowired 46 | private MockMvc mockMvc; 47 | 48 | private RequestDto requestDto; 49 | private RequestOutputDto requestOutputDto; 50 | 51 | @BeforeEach 52 | void setup() { 53 | requestDto = new RequestDto(1, "ewm-main-service", "/events/1", "192.163.0.1", LocalDateTime.now()); 54 | requestOutputDto = new RequestOutputDto("ewm-main-service", "/events/1", 1L); 55 | } 56 | 57 | @Test 58 | void addRequest() throws Exception { 59 | mockMvc.perform(post("/hit") 60 | .content(objectMapper.writeValueAsString(requestDto)) 61 | .contentType(MediaType.APPLICATION_JSON) 62 | .accept(MediaType.APPLICATION_JSON)) 63 | .andExpect(status().isCreated()); 64 | 65 | verify(statsService, times(1)).addRequest(any()); 66 | } 67 | 68 | @Test 69 | void getStats() throws Exception { 70 | String startDT = LocalDateTime.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 71 | String endDT = LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 72 | List uris = List.of(requestDto.getUri()); 73 | Boolean unique = false; 74 | 75 | when(statsService.getRequestsWithViews(any(), any(), any(), any())).thenReturn(List.of(requestOutputDto)); 76 | 77 | mockMvc.perform(get("/stats") 78 | .param("start", startDT) 79 | .param("end", endDT) 80 | .param("uris", String.join(",", uris)) 81 | .param("unique", unique.toString())) 82 | .andExpect(status().isOk()) 83 | .andExpect(jsonPath("$.[0].app", is(requestOutputDto.getApp()))) 84 | .andExpect(jsonPath("$.[0].uri", is(requestOutputDto.getUri()))) 85 | .andExpect(jsonPath("$.[0].hits", is(requestOutputDto.getHits().intValue()))); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.compilation.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.dao.EmptyResultDataAccessException; 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.compilation.dto.NewCompilationDto; 10 | import ru.practicum.compilation.dto.UpdateCompilationRequest; 11 | import ru.practicum.compilation.mapper.CompilationMapper; 12 | import ru.practicum.compilation.model.Compilation; 13 | import ru.practicum.compilation.repository.CompilationRepository; 14 | import ru.practicum.event.model.Event; 15 | import ru.practicum.event.repository.EventRepository; 16 | import ru.practicum.exception.ObjectNotFoundException; 17 | 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | @Service 22 | @RequiredArgsConstructor 23 | public class CompilationServiceImpl implements CompilationService { 24 | private final CompilationRepository compilationRepository; 25 | private final EventRepository eventRepository; 26 | private final CompilationMapper compilationMapper; 27 | 28 | @Override 29 | public Compilation add(NewCompilationDto compilationDto) { 30 | Compilation compilation = compilationMapper.newCompilationDtoToCompilation(compilationDto); 31 | 32 | if (compilationDto.getEvents() != null) { 33 | List events = eventRepository.findAllByIdIn(compilationDto.getEvents()); 34 | compilation.setEvents(events); 35 | } 36 | 37 | return compilationRepository.save(compilation); 38 | } 39 | 40 | @Override 41 | public Compilation update(Long compId, UpdateCompilationRequest compRequest) { 42 | Compilation compilation = compilationRepository.findById(compId).orElseThrow(() -> { 43 | throw new ObjectNotFoundException("Compilation with id = " + compId + " doesn't exist."); 44 | }); 45 | 46 | updateComp(compilation, compRequest); 47 | 48 | return compilationRepository.save(compilation); 49 | } 50 | 51 | @Override 52 | public Compilation get(Long compId) { 53 | return compilationRepository.findById(compId).orElseThrow(() -> { 54 | throw new ObjectNotFoundException("Compilation with id = " + compId + " doesn't exist."); 55 | }); 56 | } 57 | 58 | @Override 59 | public List getAll(Boolean pinned, Integer from, Integer size) { 60 | Sort sort = Sort.by("id").ascending(); 61 | Pageable pageable = PageRequest.of(from / size, size, sort); 62 | 63 | if (pinned != null) { 64 | return compilationRepository.findAllByPinned(pinned, pageable); 65 | } else { 66 | return compilationRepository.findAll(pageable).stream() 67 | .collect(Collectors.toList()); 68 | } 69 | } 70 | 71 | @Override 72 | public void delete(Long compId) { 73 | try { 74 | compilationRepository.deleteById(compId); 75 | } catch (EmptyResultDataAccessException e) { 76 | throw new ObjectNotFoundException("Compilation with id = " + compId + " doesn't exist."); 77 | } 78 | } 79 | 80 | private void updateComp(Compilation compilation, UpdateCompilationRequest compRequest) { 81 | if (compRequest.getEvents() != null) { 82 | List events = eventRepository.findAllByIdIn(compRequest.getEvents()); 83 | compilation.setEvents(events); 84 | } 85 | 86 | if (compRequest.getTitle() != null) { 87 | compilation.setTitle(compRequest.getTitle()); 88 | } 89 | 90 | if (compRequest.getPinned() != null) { 91 | compilation.setPinned(compRequest.getPinned()); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /main-service/src/main/resources/static/css/add-event.css: -------------------------------------------------------------------------------- 1 | .content-container { 2 | display: block; 3 | width: 45%; 4 | margin: 100px 15% 60px; 5 | padding: 30px 0; 6 | } 7 | 8 | .add-event-header { 9 | font-size: 28px; 10 | font-weight: 700; 11 | margin-bottom: 40px; 12 | } 13 | 14 | .add-event-content-block { 15 | display: block; 16 | margin-top: 40px; 17 | } 18 | 19 | .add-event-fields { 20 | margin: 20px 0; 21 | width: fit-content; 22 | } 23 | 24 | .add-event-field:not(.location) { 25 | display: grid; 26 | column-gap: 10px; 27 | row-gap: 10px; 28 | grid-template-areas: 29 | "AB" 30 | "CC"; 31 | 32 | align-items: center; 33 | /*grid-template-columns: 250px auto;*/ 34 | } 35 | 36 | .add-event-field:not(:last-of-type) { 37 | margin: 30px 0; 38 | padding-bottom: 20px; 39 | border-bottom: var(--font-color) solid 1px; 40 | } 41 | 42 | .add-event-label { 43 | font-size: 16px; 44 | font-weight: 500; 45 | grid-area: A; 46 | grid-column: 1; 47 | grid-row: 1; 48 | width: 200px; 49 | } 50 | 51 | .add-event-input { 52 | display: block; 53 | width: 400px; 54 | height: 26px; 55 | background: var(--form-background); 56 | border: var(--accent-background-color) 2px solid; 57 | border-radius: 10px; 58 | padding: 3px 12px; 59 | grid-area: B; 60 | grid-column: 2; 61 | grid-row: 1; 62 | justify-self: end; 63 | } 64 | 65 | .add-event-input#description, 66 | .add-event-input#annotation { 67 | height: fit-content; 68 | resize: none; 69 | } 70 | 71 | .add-event-input#category, 72 | .add-event-input#date { 73 | width: fit-content; 74 | } 75 | 76 | .add-event-input#lat, 77 | .add-event-input#lon { 78 | width: 170px; 79 | justify-self: end; 80 | } 81 | 82 | .add-event-input#participants { 83 | width: 80px; 84 | justify-self: end; 85 | } 86 | 87 | .add-event-input#paid, 88 | .add-event-input#requests { 89 | width: 30px; 90 | justify-self: end; 91 | } 92 | 93 | .add-event-note { 94 | font-size: 14px; 95 | font-weight: 500; 96 | grid-area: C; 97 | grid-column: 2 / 3; 98 | grid-row: 2; 99 | text-align: end; 100 | color: var(--accent-color); 101 | } 102 | 103 | .add-event-field + .location { 104 | display: grid; 105 | column-gap: 10px; 106 | row-gap: 10px; 107 | grid-template-areas: 108 | "AB" 109 | "CD" 110 | "EE"; 111 | /*margin: 20px 0;*/ 112 | margin: 30px 0; 113 | padding-bottom: 20px; 114 | border-bottom: var(--font-color) solid 1px; 115 | } 116 | 117 | .location-lat-label { 118 | grid-area: A; 119 | grid-column: 1; 120 | grid-row: 1; 121 | } 122 | 123 | .location-lat-input { 124 | display: block; 125 | height: 26px; 126 | width: auto; 127 | background: var(--form-background); 128 | border: var(--accent-background-color) 2px solid; 129 | border-radius: 10px; 130 | padding: 3px 12px; 131 | grid-area: B; 132 | grid-column: 2; 133 | grid-row: 1; 134 | } 135 | 136 | .location-lon-label { 137 | font-size: 16px; 138 | font-weight: 500; 139 | grid-area: C; 140 | grid-column: 1; 141 | grid-row: 2; 142 | } 143 | 144 | .location-lon-input { 145 | display: block; 146 | height: 26px; 147 | width: auto; 148 | background: var(--form-background); 149 | border: var(--accent-background-color) 2px solid; 150 | border-radius: 10px; 151 | padding: 3px 12px; 152 | grid-area: D; 153 | grid-column: 2; 154 | grid-row: 2; 155 | } 156 | 157 | .location-note { 158 | font-size: 14px; 159 | font-weight: 500; 160 | grid-area: E; 161 | grid-column: 1 / 3; 162 | grid-row: 3; 163 | text-align: end; 164 | } 165 | 166 | .add-event-btn { 167 | margin-top: 40px; 168 | background: var(--accent-color); 169 | padding: 10px 30px; 170 | font-size: 16px; 171 | font-weight: 600; 172 | color: #FFF; 173 | text-decoration: none; 174 | border-radius: 10px; 175 | cursor: pointer; 176 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/controller/StatsController.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.ResponseStatus; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import ru.practicum.RequestDto; 16 | import ru.practicum.RequestOutputDto; 17 | import ru.practicum.service.StatsServiceImpl; 18 | 19 | import javax.validation.Valid; 20 | import java.time.LocalDateTime; 21 | import java.time.format.DateTimeFormatter; 22 | import java.time.format.DateTimeParseException; 23 | import java.util.List; 24 | 25 | @RestController 26 | @RequiredArgsConstructor 27 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 28 | @Slf4j 29 | public class StatsController { 30 | 31 | private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 32 | 33 | private final StatsServiceImpl statsService; 34 | 35 | @PostMapping("/hit") 36 | @ResponseStatus(code = HttpStatus.CREATED) 37 | public void addRequest(@Valid @RequestBody RequestDto requestDto) { 38 | log.info("Calling POST: /hit with 'RequestDto': {}", requestDto.toString()); 39 | statsService.addRequest(requestDto); 40 | } 41 | 42 | @GetMapping("/stats") 43 | public ResponseEntity> getStats(@RequestParam String start, 44 | @RequestParam String end, 45 | @RequestParam(required = false) List uris, 46 | @RequestParam(defaultValue = "false") Boolean unique) { 47 | 48 | log.info("Calling GET: /stats with 'start': {}, 'end': {}, uris: {}, unique: {}", start, end, uris, unique); 49 | 50 | LocalDateTime startDT; 51 | LocalDateTime endDT; 52 | try { 53 | startDT = LocalDateTime.parse(start, DTF); 54 | endDT = LocalDateTime.parse(end, DTF); 55 | } catch (DateTimeParseException e) { 56 | return ResponseEntity.badRequest().build(); 57 | } 58 | 59 | if (startDT.isAfter(endDT)) { 60 | return ResponseEntity.badRequest().build(); 61 | } 62 | 63 | List results = statsService.getRequestsWithViews(startDT, endDT, uris, unique); 64 | return ResponseEntity.ok().body(results); 65 | } 66 | 67 | @GetMapping("/statsByIp") 68 | public ResponseEntity> statsByIp(@RequestParam String start, 69 | @RequestParam String end, 70 | @RequestParam(required = false) List uris, 71 | @RequestParam(defaultValue = "false") Boolean unique, 72 | @RequestParam String ip) { 73 | 74 | log.info("Calling GET: /statsByIp with 'start': {}, 'end': {}, uris: {}, unique: {}, ip: {}", start, end, uris, unique, ip); 75 | 76 | LocalDateTime startDT; 77 | LocalDateTime endDT; 78 | try { 79 | startDT = LocalDateTime.parse(start, DTF); 80 | endDT = LocalDateTime.parse(end, DTF); 81 | } catch (DateTimeParseException e) { 82 | return ResponseEntity.badRequest().build(); 83 | } 84 | 85 | List results = statsService.getRequestsWithViewsByIp(startDT, endDT, uris, unique, ip); 86 | return ResponseEntity.ok().body(results); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /stats-service/server/src/main/test/java/ru/practicum/StatsServiceTest.java: -------------------------------------------------------------------------------- 1 | package ru.practicum; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.mockito.InjectMocks; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.Spy; 10 | import org.mockito.junit.jupiter.MockitoExtension; 11 | import org.springframework.test.context.TestPropertySource; 12 | import ru.practicum.mapper.RequestMapperImpl; 13 | import ru.practicum.model.App; 14 | import ru.practicum.model.Request; 15 | import ru.practicum.repository.AppRepository; 16 | import ru.practicum.repository.RequestRepository; 17 | import ru.practicum.service.StatsServiceImpl; 18 | 19 | import java.time.LocalDateTime; 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | import static org.junit.jupiter.api.Assertions.*; 24 | import static org.mockito.ArgumentMatchers.any; 25 | import static org.mockito.Mockito.atMostOnce; 26 | import static org.mockito.Mockito.verify; 27 | 28 | @ExtendWith(MockitoExtension.class) 29 | @TestPropertySource(properties = {"db.name=test"}) 30 | public class StatsServiceTest { 31 | 32 | @InjectMocks 33 | private StatsServiceImpl statsService; 34 | 35 | @Mock 36 | private RequestRepository requestRepository; 37 | 38 | @Mock 39 | private AppRepository appRepository; 40 | 41 | @Spy 42 | private RequestMapperImpl requestMapper; 43 | 44 | RequestDto requestDto; 45 | 46 | RequestOutputDto requestOutputDto; 47 | Request request; 48 | App app; 49 | 50 | @BeforeEach 51 | void setup() { 52 | request = new Request(1, new App("main-service"), "/events/1", "192.163.0.1", LocalDateTime.now()); 53 | requestDto = new RequestDto(1, "main-service", "/events/1", "192.163.0.1", LocalDateTime.now()); 54 | requestOutputDto = new RequestOutputDto("main-service", "/events/1", 1L); 55 | app = new App(requestDto.getApp()); 56 | } 57 | 58 | @Test 59 | void addRequest() { 60 | Mockito.when(appRepository.findByName(requestDto.getApp())) 61 | .thenReturn(Optional.ofNullable(app)); 62 | 63 | Mockito.when(requestRepository.save(any())) 64 | .thenReturn(request); 65 | 66 | statsService.addRequest(requestDto); 67 | 68 | verify(requestRepository, atMostOnce()).saveAndFlush(any()); 69 | } 70 | 71 | @Test 72 | void getRequestsWithViews_WhenUnique() { 73 | Mockito.when(requestRepository.getUniqueIpRequestsWithUri(any(), any(), any())) 74 | .thenReturn(List.of(requestOutputDto)); 75 | 76 | List requestOutputDtoSaved = statsService.getRequestsWithViews(LocalDateTime.now().minusDays(1), 77 | LocalDateTime.now().plusDays(1), List.of("/events/1"), true); 78 | 79 | assertAll( 80 | () -> assertEquals(requestOutputDtoSaved.size(), 1), 81 | () -> assertEquals(requestOutputDtoSaved.get(0).getUri(), request.getUri()), 82 | () -> assertEquals(requestOutputDtoSaved.get(0).getApp(), app.getName()), 83 | () -> assertEquals(requestOutputDtoSaved.get(0).getHits(), 1L) 84 | ); 85 | 86 | verify(requestRepository, atMostOnce()).saveAndFlush(any()); 87 | } 88 | 89 | @Test 90 | void getRequestsWithViews_WhenNotUnique() { 91 | Mockito.when(requestRepository.getAllRequestsWithUri(any(), any(), any())) 92 | .thenReturn(List.of(requestOutputDto)); 93 | 94 | List requestOutputDtoSaved = statsService.getRequestsWithViews(LocalDateTime.now().minusDays(1), 95 | LocalDateTime.now().plusDays(1), List.of("/events/1"), false); 96 | 97 | assertAll( 98 | () -> assertEquals(requestOutputDtoSaved.size(), 1), 99 | () -> assertEquals(requestOutputDtoSaved.get(0).getUri(), request.getUri()), 100 | () -> assertEquals(requestOutputDtoSaved.get(0).getApp(), app.getName()), 101 | () -> assertEquals(requestOutputDtoSaved.get(0).getHits(), 1L) 102 | ); 103 | 104 | verify(requestRepository, atMostOnce()).saveAndFlush(any()); 105 | } 106 | } -------------------------------------------------------------------------------- /stats-service/server/src/main/java/ru/practicum/repository/RequestRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | import ru.practicum.RequestOutputDto; 6 | import ru.practicum.model.Request; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | 11 | public interface RequestRepository extends JpaRepository { 12 | 13 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(r.ip)) " + 14 | "FROM Request as r " + 15 | "LEFT JOIN App as a ON a.id = r.app.id " + 16 | "WHERE r.timestamp between ?1 AND ?2 " + 17 | "AND r.uri IN (?3) " + 18 | "GROUP BY a.name, r.uri " + 19 | "ORDER BY COUNT(r.ip) DESC ") 20 | List getAllRequestsWithUri(LocalDateTime start, LocalDateTime end, List uris); 21 | 22 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(DISTINCT r.ip)) " + 23 | "FROM Request as r " + 24 | "LEFT JOIN App as a ON a.id = r.app.id " + 25 | "WHERE r.timestamp between ?1 AND ?2 " + 26 | "AND r.uri IN (?3) " + 27 | "GROUP BY a.name, r.uri " + 28 | "ORDER BY COUNT(DISTINCT r.ip) DESC ") 29 | List getUniqueIpRequestsWithUri(LocalDateTime start, LocalDateTime end, List uris); 30 | 31 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(DISTINCT r.ip)) " + 32 | "FROM Request as r " + 33 | "LEFT JOIN App as a ON a.id = r.app.id " + 34 | "WHERE r.timestamp between ?1 AND ?2 " + 35 | "GROUP BY a.name, r.uri " + 36 | "ORDER BY COUNT(DISTINCT r.ip) DESC ") 37 | List getUniqueIpRequestsWithoutUri(LocalDateTime start, LocalDateTime end); 38 | 39 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(r.ip)) " + 40 | "FROM Request as r " + 41 | "LEFT JOIN App as a ON a.id = r.app.id " + 42 | "WHERE r.timestamp between ?1 AND ?2 " + 43 | "GROUP BY a.name, r.uri " + 44 | "ORDER BY COUNT(r.ip) DESC ") 45 | List getAllRequestsWithoutUri(LocalDateTime start, LocalDateTime end); 46 | 47 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(r.ip)) " + 48 | "FROM Request as r " + 49 | "LEFT JOIN App as a ON a.id = r.app.id " + 50 | "WHERE r.timestamp between ?1 AND ?2 " + 51 | "AND r.uri IN (?3) " + 52 | "AND r.ip = ?4 " + 53 | "GROUP BY a.name, r.uri " + 54 | "ORDER BY COUNT(r.ip) DESC ") 55 | List getAllRequestsWithUriByIp(LocalDateTime start, LocalDateTime end, List uris, String ip); 56 | 57 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(DISTINCT r.ip)) " + 58 | "FROM Request as r " + 59 | "LEFT JOIN App as a ON a.id = r.app.id " + 60 | "WHERE r.timestamp between ?1 AND ?2 " + 61 | "AND r.uri IN (?3) " + 62 | "AND r.ip = ?4 " + 63 | "GROUP BY a.name, r.uri " + 64 | "ORDER BY COUNT(DISTINCT r.ip) DESC ") 65 | List getUniqueIpRequestsWithUriByIp(LocalDateTime start, LocalDateTime end, List uris, String ip); 66 | 67 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(DISTINCT r.ip)) " + 68 | "FROM Request as r " + 69 | "LEFT JOIN App as a ON a.id = r.app.id " + 70 | "WHERE r.timestamp between ?1 AND ?2 " + 71 | "AND r.ip = ?3 " + 72 | "GROUP BY a.name, r.uri " + 73 | "ORDER BY COUNT(DISTINCT r.ip) DESC ") 74 | List getUniqueIpRequestsWithoutUriByIp(LocalDateTime start, LocalDateTime end, String ip); 75 | 76 | @Query(value = "SELECT new ru.practicum.RequestOutputDto(a.name, r.uri, COUNT(r.ip)) " + 77 | "FROM Request as r " + 78 | "LEFT JOIN App as a ON a.id = r.app.id " + 79 | "WHERE r.timestamp between ?1 AND ?2 " + 80 | "AND r.ip = ?3 " + 81 | "GROUP BY a.name, r.uri " + 82 | "ORDER BY COUNT(r.ip) DESC ") 83 | List getAllRequestsWithoutUriByIp(LocalDateTime start, LocalDateTime end, String ip); 84 | } 85 | -------------------------------------------------------------------------------- /main-service/src/main/resources/templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Explore with me - Настройки аккаунта 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |

Настройки аккаунта

22 | 23 |
24 | 25 |

Данные аккаунта

26 | 27 |
28 | Имя 29 | 30 |
31 | 32 |
33 | Имя пользователя 34 | 35 |
36 | 37 |
38 | Email 39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 |

Изменить данные аккаунта

47 | 48 |
49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 | 58 |
59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |
69 | 70 | 71 | 72 |
73 | 74 |
75 | 76 |
77 | 78 |

Удалить аккаунт

79 | 80 |
81 |

Удаление аккаунта произойдёт в течение 14 дней и затронет все ваши данные: созданные события, 82 | комментарии и другие данные. После выбора этой функции вы не сможете восстановить аккаунт.

83 |
84 | 85 | 86 | 87 |
88 | 89 |
90 | 91 |
92 | 93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/model/Event.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.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 lombok.ToString; 9 | import ru.practicum.category.model.Category; 10 | import ru.practicum.compilation.model.Compilation; 11 | import ru.practicum.event.model.enums.EventState; 12 | import ru.practicum.user.model.User; 13 | 14 | import javax.persistence.CascadeType; 15 | import javax.persistence.Column; 16 | import javax.persistence.Entity; 17 | import javax.persistence.EnumType; 18 | import javax.persistence.Enumerated; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.GenerationType; 21 | import javax.persistence.Id; 22 | import javax.persistence.JoinColumn; 23 | import javax.persistence.JoinTable; 24 | import javax.persistence.ManyToMany; 25 | import javax.persistence.ManyToOne; 26 | import javax.persistence.Table; 27 | import javax.validation.constraints.Size; 28 | import java.time.LocalDateTime; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import java.util.Objects; 32 | 33 | @Entity 34 | @Table(name = "events") 35 | @Getter 36 | @Setter 37 | @ToString 38 | @NoArgsConstructor 39 | @AllArgsConstructor 40 | public class Event { 41 | 42 | @Id 43 | @GeneratedValue(strategy = GenerationType.IDENTITY) 44 | private Long id; 45 | 46 | @Size(min = 20, max = 2000) 47 | private String annotation; 48 | 49 | @ManyToOne(cascade = CascadeType.ALL) 50 | @JoinColumn(name = "cat_id") 51 | private Category category; 52 | 53 | @Column(name = "confirmed_requests") 54 | private Long confirmedRequests = 0L; 55 | 56 | @Column(name = "created_on") 57 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 58 | private LocalDateTime createdOn; 59 | 60 | @Size(min = 20, max = 7000) 61 | private String description; 62 | 63 | @Column(name = "event_date") 64 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 65 | private LocalDateTime eventDate; 66 | 67 | @ManyToOne(cascade = CascadeType.ALL) 68 | @JoinColumn(name = "initiator") 69 | private User initiator; 70 | 71 | @ManyToOne(cascade = CascadeType.ALL) 72 | @JoinColumn(name = "location") 73 | private Location location; 74 | 75 | private Boolean paid; 76 | 77 | @Column(name = "participant_limit") 78 | private Long participantLimit = 0L; 79 | 80 | @Column(name = "published_on") 81 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 82 | private LocalDateTime publishedOn; 83 | 84 | @Column(name = "request_moderation") 85 | private Boolean requestModeration; 86 | 87 | @Enumerated(EnumType.STRING) 88 | private EventState state; 89 | 90 | @Size(min = 3, max = 120) 91 | private String title; 92 | 93 | private Long views = 0L; 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (this == o) return true; 98 | if (o == null || !(o instanceof Event)) return false; 99 | Event event = (Event) o; 100 | return Objects.equals(id, event.id) && Objects.equals(annotation, event.annotation) && Objects.equals(category, event.category) && Objects.equals(confirmedRequests, event.confirmedRequests) && Objects.equals(createdOn, event.createdOn) && Objects.equals(description, event.description) && Objects.equals(eventDate, event.eventDate) && Objects.equals(initiator, event.initiator) && Objects.equals(location, event.location) && Objects.equals(paid, event.paid) && Objects.equals(participantLimit, event.participantLimit) && Objects.equals(publishedOn, event.publishedOn) && Objects.equals(requestModeration, event.requestModeration) && state == event.state && Objects.equals(title, event.title) && Objects.equals(views, event.views) && Objects.equals(compilations, event.compilations); 101 | } 102 | 103 | @Override 104 | public int hashCode() { 105 | return Objects.hash(id, annotation, category, confirmedRequests, createdOn, description, eventDate, initiator, location, paid, participantLimit, publishedOn, requestModeration, state, title, views, compilations); 106 | } 107 | 108 | @ManyToMany(cascade = CascadeType.ALL) 109 | @JoinTable( 110 | name = "comp_events", 111 | joinColumns = @JoinColumn(name = "event_id"), 112 | inverseJoinColumns = @JoinColumn(name = "compilation_id")) 113 | private List compilations = new ArrayList<>(); 114 | } -------------------------------------------------------------------------------- /stats-service/server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | ru.practicum 9 | stats-service 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | server 14 | jar 15 | 16 | 17 | 11 18 | 11 19 | UTF-8 20 | 21 | 22 | 23 | 24 | ru.practicum 25 | dto 26 | 0.0.1-SNAPSHOT 27 | compile 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-validation 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-actuator 43 | 44 | 45 | 46 | org.apache.httpcomponents 47 | httpclient 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-configuration-processor 53 | 54 | 55 | 56 | org.projectlombok 57 | lombok 58 | true 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-data-jpa 64 | 2.7.5 65 | 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-test 70 | test 71 | 72 | 73 | 74 | org.postgresql 75 | postgresql 76 | runtime 77 | 78 | 79 | 80 | com.h2database 81 | h2 82 | runtime 83 | 84 | 85 | 86 | org.mapstruct 87 | mapstruct 88 | 1.5.3.Final 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-maven-plugin 98 | 2.7.5 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-compiler-plugin 103 | 3.8.1 104 | 105 | 11 106 | 11 107 | 108 | 109 | org.projectlombok 110 | lombok 111 | ${lombok.version} 112 | 113 | 114 | org.mapstruct 115 | mapstruct-processor 116 | 1.5.3.Final 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/event/repository/EventSpecRepository.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.event.repository; 2 | 3 | import org.springframework.data.jpa.domain.Specification; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 | import org.springframework.stereotype.Repository; 7 | import ru.practicum.event.model.Event; 8 | import ru.practicum.event.model.enums.EventState; 9 | 10 | import javax.persistence.criteria.CriteriaBuilder; 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | 14 | @Repository 15 | public interface EventSpecRepository extends JpaRepository, JpaSpecificationExecutor { 16 | 17 | static Specification hasText(String text) { 18 | return (event, query, critBuilder) -> { 19 | if (text == null) { 20 | return critBuilder.isTrue(critBuilder.literal(true)); 21 | } else { 22 | return critBuilder.or(critBuilder.like(critBuilder.lower(event.get("annotation")), text.toLowerCase()), 23 | critBuilder.like(critBuilder.lower(event.get("description")), text.toLowerCase())); 24 | } 25 | }; 26 | } 27 | 28 | static Specification hasCategories(List categories) { 29 | return (event, query, critBuilder) -> { 30 | if (categories == null || categories.size() == 0) { 31 | return critBuilder.isTrue(critBuilder.literal(true)); 32 | } else { 33 | CriteriaBuilder.In categoryIds = critBuilder.in(event.get("category")); 34 | for (long catId : categories) { 35 | categoryIds.value(catId); 36 | } 37 | return categoryIds; 38 | } 39 | }; 40 | } 41 | 42 | static Specification hasUsers(List users) { 43 | return (event, query, critBuilder) -> { 44 | if (users == null || users.size() == 0) { 45 | return critBuilder.isTrue(critBuilder.literal(true)); 46 | } else { 47 | CriteriaBuilder.In userIds = critBuilder.in(event.get("initiator")); 48 | for (long userId : users) { 49 | userIds.value(userId); 50 | } 51 | return userIds; 52 | } 53 | }; 54 | } 55 | 56 | static Specification hasStates(List states) { 57 | return (event, query, critBuilder) -> { 58 | if (states == null || states.size() == 0) { 59 | return critBuilder.isTrue(critBuilder.literal(true)); 60 | } else { 61 | CriteriaBuilder.In cbStates = critBuilder.in(event.get("state")); 62 | for (EventState state : states) { 63 | cbStates.value(state); 64 | } 65 | return cbStates; 66 | } 67 | }; 68 | } 69 | 70 | static Specification hasRangeStart(LocalDateTime rangeStart) { 71 | return (event, query, critBuilder) -> { 72 | if (rangeStart == null) { 73 | return critBuilder.isTrue(critBuilder.literal(true)); 74 | } else { 75 | return critBuilder.greaterThan(event.get("eventDate"), rangeStart); 76 | } 77 | }; 78 | } 79 | 80 | static Specification hasRangeEnd(LocalDateTime rangeEnd) { 81 | return (event, query, critBuilder) -> { 82 | if (rangeEnd == null) { 83 | return critBuilder.isTrue(critBuilder.literal(true)); 84 | } else { 85 | return critBuilder.lessThan(event.get("eventDate"), rangeEnd); 86 | } 87 | }; 88 | } 89 | 90 | static Specification hasPaid(Boolean paid) { 91 | return (event, query, critBuilder) -> { 92 | if (paid == null) { 93 | return critBuilder.isTrue(critBuilder.literal(true)); 94 | } else { 95 | return critBuilder.equal(event.get("paid"), paid); 96 | } 97 | }; 98 | } 99 | 100 | static Specification hasAvailable(Boolean onlyAvailable) { 101 | return (event, query, critBuilder) -> { 102 | if (onlyAvailable != null && onlyAvailable) { 103 | return critBuilder.or(critBuilder.le(event.get("confirmedRequests"), event.get("participantLimit")), 104 | critBuilder.le(event.get("participantLimit"), 0)); 105 | } else { 106 | return critBuilder.isTrue(critBuilder.literal(true)); 107 | } 108 | }; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /main-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ru.practicum.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.dao.DataIntegrityViolationException; 6 | import org.springframework.dao.EmptyResultDataAccessException; 7 | import org.springframework.data.domain.PageRequest; 8 | import org.springframework.data.domain.Pageable; 9 | import org.springframework.data.domain.Sort; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.stereotype.Service; 12 | import ru.practicum.exception.ObjectNotFoundException; 13 | import ru.practicum.exception.SQLConstraintViolationException; 14 | import ru.practicum.user.dto.NewUserDto; 15 | import ru.practicum.user.dto.UserDto; 16 | import ru.practicum.user.mapper.UserMapper; 17 | import ru.practicum.user.model.Role; 18 | import ru.practicum.user.model.User; 19 | import ru.practicum.user.repository.RoleRepository; 20 | import ru.practicum.user.repository.UserRepository; 21 | 22 | import javax.persistence.EntityManager; 23 | import javax.persistence.PersistenceContext; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.stream.Collectors; 27 | 28 | @Service 29 | @RequiredArgsConstructor 30 | @Slf4j 31 | public class UserServiceImpl implements UserService { 32 | 33 | @PersistenceContext 34 | private EntityManager em; 35 | private final UserRepository userRepository; 36 | private final RoleRepository roleRepository; 37 | private final UserMapper userMapper; 38 | 39 | @Override 40 | public User addAdminUser(User user) throws SQLConstraintViolationException { 41 | 42 | Role role = roleRepository.getByName("ROLE_USER").orElseThrow(() -> { 43 | throw new ObjectNotFoundException("Role with name = 'USER' doesn't exist."); 44 | }); 45 | 46 | user.setRoles(Collections.singleton(role)); 47 | 48 | try { 49 | return userRepository.save(user); 50 | } catch (DataIntegrityViolationException e) { 51 | throw new SQLConstraintViolationException("User name and/or email already exists."); 52 | } 53 | } 54 | 55 | public UserDto get(Long userId) { 56 | User user = userRepository.findById(userId).orElseThrow(() -> new ObjectNotFoundException("User with id = " + userId + "doesn't exist.")); 57 | return userMapper.userToUserDto(user); 58 | } 59 | 60 | @Override 61 | public List getAll() { 62 | return userRepository.findAll(); 63 | } 64 | 65 | @Override 66 | public List getByIds(List ids, Integer from, Integer size) { 67 | Pageable pageable = PageRequest.of(from / size, size, Sort.by("id").ascending()); 68 | 69 | if (ids == null) { 70 | return userRepository.findAll(pageable).stream().collect(Collectors.toList()); 71 | } else { 72 | return userRepository.findAllByIdIn(ids, pageable).stream().collect(Collectors.toList()); 73 | } 74 | } 75 | 76 | @Override 77 | public void delete(Long userId) { 78 | try { 79 | userRepository.deleteById(userId); 80 | } catch (EmptyResultDataAccessException e) { 81 | throw new ObjectNotFoundException("User with id = " + userId + " was not found."); 82 | } 83 | } 84 | 85 | public User findByEmail(String email) throws UsernameNotFoundException { 86 | return userRepository.findByEmail(email).orElseThrow(() -> new ObjectNotFoundException("User with email = " + email + " doesn't exist.")); 87 | } 88 | 89 | public User loadUserByUsername(String username) throws UsernameNotFoundException { 90 | return userRepository.findByUsername(username).orElseThrow(() -> new ObjectNotFoundException("User with username = " + username + " doesn't exist.")); 91 | } 92 | 93 | @Override 94 | public List getUsersWithIdBiggerThan(Long idMin) { 95 | return em.createQuery("SELECT u FROM User u WHERE u.id > :paramId", User.class).setParameter("paramId", idMin).getResultList(); 96 | } 97 | 98 | @Override 99 | public User add(NewUserDto newUser) throws SQLConstraintViolationException { 100 | User user = userMapper.newUserRequestDtoToUser(newUser); 101 | 102 | Role role = roleRepository.getByName("ROLE_USER").orElseThrow(() -> { 103 | throw new ObjectNotFoundException("Role 'USER' doesn't exist."); 104 | }); 105 | 106 | user.setRoles(Collections.singleton(role)); 107 | user.setPassword(newUser.getPassword()); 108 | 109 | user = userRepository.save(user); 110 | 111 | log.info("Создан пользователь с id = " + user.getId()); 112 | 113 | return user; 114 | } 115 | } 116 | --------------------------------------------------------------------------------