diff --git a/README.md b/README.md index 18a246e..17e8964 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # java-explore-with-me -Template repository for ExploreWithMe project. +## πŸ“Œ Бсылка Π½Π° Pull Request + +https://github.com/russuAV/java-explore-with-me/pull/8 \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java b/ewm-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java index 43f6ea1..6ff40f4 100644 --- a/ewm-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java +++ b/ewm-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java @@ -32,4 +32,4 @@ CategoryDto update( @Valid @RequestBody NewCategoryDto newCategoryDto) { return categoryService.update(catId, newCategoryDto); } -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java b/ewm-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java index a953e2b..04e8330 100644 --- a/ewm-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java +++ b/ewm-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java @@ -11,4 +11,4 @@ public interface CategoryMapper { CategoryDto toCategoryDto(Category category); Category toCategory(NewCategoryDto newCategoryDto); -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/category/model/Category.java b/ewm-service/src/main/java/ru/practicum/category/model/Category.java index fd46d20..2718f00 100644 --- a/ewm-service/src/main/java/ru/practicum/category/model/Category.java +++ b/ewm-service/src/main/java/ru/practicum/category/model/Category.java @@ -2,10 +2,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; @Getter @Setter @@ -13,6 +10,7 @@ @Table(name = "categories") @AllArgsConstructor @NoArgsConstructor +@Builder public class Category { @Id diff --git a/ewm-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java b/ewm-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java index 6587cb7..f0e2544 100644 --- a/ewm-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java +++ b/ewm-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java @@ -1,8 +1,11 @@ package ru.practicum.category.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import ru.practicum.category.model.Category; +import java.util.List; import java.util.Optional; public interface CategoryRepository extends JpaRepository { @@ -10,4 +13,7 @@ public interface CategoryRepository extends JpaRepository { boolean existsByName(String name); Optional findByName(String name); + + @Query(value = "SELECT * FROM categories ORDER BY id LIMIT :size OFFSET :from", nativeQuery = true) + List findWithOffset(@Param("from") int from, @Param("size") int size); } diff --git a/ewm-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java b/ewm-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java index b911fb6..f8e35d3 100644 --- a/ewm-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java @@ -2,20 +2,19 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import ru.practicum.category.mapper.CategoryMapper; -import ru.practicum.category.repository.CategoryRepository; import ru.practicum.category.model.Category; import ru.practicum.category.model.CategoryDto; import ru.practicum.category.model.NewCategoryDto; +import ru.practicum.category.repository.CategoryRepository; import ru.practicum.event.repository.EventRepository; import ru.practicum.exception.ConflictException; import ru.practicum.exception.NotFoundException; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service @Slf4j @@ -84,11 +83,10 @@ public CategoryDto getCategoryById(Long catId) { @Override public List getCategories(int from, int size) { - Pageable pageable = PageRequest.of(from / size, size); - List categories = categoryRepository.findAll(pageable).getContent(); - - return categories.stream() + return categoryRepository.findWithOffset(from, size) + .stream() .map(categoryMapper::toCategoryDto) - .toList(); + .collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java b/ewm-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java index 94830ad..dd8ae9e 100644 --- a/ewm-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java +++ b/ewm-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java @@ -18,4 +18,4 @@ public interface CompilationMapper { @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) @Mapping(target = "events", ignore = true) void updateCompilationFromDto(UpdateCompilationRequest dto, @MappingTarget Compilation compilation); -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/model/Compilation.java b/ewm-service/src/main/java/ru/practicum/compilation/model/Compilation.java index 6acc52c..ae19b16 100644 --- a/ewm-service/src/main/java/ru/practicum/compilation/model/Compilation.java +++ b/ewm-service/src/main/java/ru/practicum/compilation/model/Compilation.java @@ -31,4 +31,4 @@ public class Compilation { ) private Set events = new HashSet<>(); -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java index f773e7b..99ff322 100644 --- a/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java +++ b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java @@ -6,6 +6,6 @@ import java.util.List; -public interface CompilationRepository extends JpaRepository { +public interface CompilationRepository extends JpaRepository, CompilationRepositoryCustom { List findAllByPinned(Boolean pinned, Pageable pageable); -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryCustom.java b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryCustom.java new file mode 100644 index 0000000..cc0c5ef --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryCustom.java @@ -0,0 +1,9 @@ +package ru.practicum.compilation.repository; + +import ru.practicum.compilation.model.Compilation; + +import java.util.List; + +public interface CompilationRepositoryCustom { + List findCompilations(Boolean pinned, int from, int size); +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryImpl.java b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryImpl.java new file mode 100644 index 0000000..ed27b0e --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/compilation/repository/CompilationRepositoryImpl.java @@ -0,0 +1,35 @@ +package ru.practicum.compilation.repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import ru.practicum.compilation.model.Compilation; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class CompilationRepositoryImpl implements CompilationRepositoryCustom { + private final EntityManager entityManager; + + @Override + public List findCompilations(Boolean pinned, int from, int size) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Compilation.class); + Root root = query.from(Compilation.class); + + if (pinned != null) { + query.where(cb.equal(root.get("pinned"), pinned)); + } + + query.orderBy(cb.desc(root.get("id"))); + + return entityManager.createQuery(query) + .setFirstResult(from) + .setMaxResults(size) + .getResultList(); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java b/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java index 727da6f..3fd7ac0 100644 --- a/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java @@ -1,9 +1,6 @@ package ru.practicum.compilation.service; import jakarta.persistence.EntityManager; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -87,21 +84,7 @@ public Compilation getEntityById(Long compId) { @Override @Transactional(readOnly = true) public List getAllCompilations(Boolean pinned, int from, int size, HttpServletRequest request) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - CriteriaQuery query = cb.createQuery(Compilation.class); - Root root = query.from(Compilation.class); - - if (pinned != null) { - query.where(cb.equal(root.get("pinned"), pinned)); - } - - query.orderBy(cb.desc(root.get("id"))); - - List compilations = entityManager.createQuery(query) - .setFirstResult(from) - .setMaxResults(size) - .getResultList(); - + List compilations = compilationRepository.findCompilations(pinned, from, size); Map viewsMap = getAllViewsForCompilations(compilations); compilations.forEach(compilation -> { @@ -118,7 +101,6 @@ public List getAllCompilations(Boolean pinned, int from, int siz @Override public CompilationDto getCompilationById(Long compId, HttpServletRequest request) { Compilation compilation = getEntityById(compId); - Map viewsMap = getAllViewsForCompilations(List.of(compilation)); compilation.getEvents().forEach(event -> diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/controller/AdminCommentController.java b/ewm-service/src/main/java/ru/practicum/event/comment/controller/AdminCommentController.java new file mode 100644 index 0000000..5982b53 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/controller/AdminCommentController.java @@ -0,0 +1,30 @@ +package ru.practicum.event.comment.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import ru.practicum.event.comment.model.AdminCommentSearchRequest; +import ru.practicum.event.comment.model.CommentFullDto; +import ru.practicum.event.comment.service.CommentService; + +import java.util.List; + +@RestController +@RequestMapping("/admin/comments") +@RequiredArgsConstructor +public class AdminCommentController { + private final CommentService commentService; + + @DeleteMapping("/{commentId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteComment(@PathVariable Long commentId) { // Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ администратором ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Ρ‹ + commentService.deleteCommentByAdmin(commentId); + } + + @GetMapping + public List getCommentsForModeration( + @Valid @ModelAttribute AdminCommentSearchRequest adminCommentSearchRequest) { + return commentService.getCommentsForModeration(adminCommentSearchRequest); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/controller/PrivateCommentController.java b/ewm-service/src/main/java/ru/practicum/event/comment/controller/PrivateCommentController.java new file mode 100644 index 0000000..defeaa1 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/controller/PrivateCommentController.java @@ -0,0 +1,50 @@ +package ru.practicum.event.comment.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import ru.practicum.event.comment.model.*; +import ru.practicum.event.comment.service.CommentService; + +import java.util.List; + +@RestController +@RequestMapping("/users/{userId}/comments") +@RequiredArgsConstructor +public class PrivateCommentController { + private final CommentService commentService; + + @PostMapping("/events/{eventId}") + @ResponseStatus(HttpStatus.CREATED) + public CommentDto addComment( + @PathVariable Long userId, + @PathVariable Long eventId, + @Valid @RequestBody NewCommentRequest newCommentRequest) { + return commentService.addComment(userId, eventId, newCommentRequest); + } + + @PatchMapping("/{commentId}") + public CommentDto updateComment( + @PathVariable Long userId, + @PathVariable Long commentId, + @Valid @RequestBody UpdateCommentRequest updateCommentRequest + ) { + return commentService.updateComment(userId, commentId, updateCommentRequest); + } + + @DeleteMapping("/{commentId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteComment( + @PathVariable Long userId, + @PathVariable Long commentId) { + commentService.deleteComment(userId, commentId); + } + + @GetMapping + public List getOwnComments( + @PathVariable Long userId, + @Valid @ModelAttribute UserCommentSearchRequest request) { + return commentService.getOwnComments(userId, request); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/mapper/CommentMapper.java b/ewm-service/src/main/java/ru/practicum/event/comment/mapper/CommentMapper.java new file mode 100644 index 0000000..9ce3564 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/mapper/CommentMapper.java @@ -0,0 +1,23 @@ +package ru.practicum.event.comment.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import ru.practicum.event.comment.model.*; +import ru.practicum.event.mapper.EventMapper; +import ru.practicum.user.mapper.UserMapper; + +@Mapper(componentModel = "spring", + uses = {UserMapper.class, EventMapper.class}) +public interface CommentMapper { + + Comment toComment(NewCommentRequest newCommentRequest); + + @Mapping(source = "author.name", target = "authorName") + CommentDto toDto(Comment comment); + + @Mapping(target = "text", source = "text") + Comment update(UpdateCommentRequest updateCommentRequest, @MappingTarget Comment comment); + + CommentFullDto toFullDto(Comment comment); +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/AdminCommentSearchRequest.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/AdminCommentSearchRequest.java new file mode 100644 index 0000000..168c040 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/AdminCommentSearchRequest.java @@ -0,0 +1,38 @@ +package ru.practicum.event.comment.model; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AdminCommentSearchRequest { + private List eventIds; + private List authorIds; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime rangeStart; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime rangeEnd; + + @PositiveOrZero + private int from = 0; + + @PositiveOrZero + private int size = 10; + + @AssertTrue(message = "Π”Π°Ρ‚Π° Π½Π°Ρ‡Π°Π»Π° Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΠΎΠ·ΠΆΠ΅ Π΄Π°Ρ‚Ρ‹ окончания") + public boolean isValidRange() { + return rangeStart == null || rangeEnd == null || !rangeStart.isAfter(rangeEnd); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/Comment.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/Comment.java new file mode 100644 index 0000000..5e60d35 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/Comment.java @@ -0,0 +1,40 @@ +package ru.practicum.event.comment.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import org.hibernate.validator.constraints.Length; +import ru.practicum.event.model.Event; +import ru.practicum.user.model.User; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "comments") +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @Length(min = 1, max = 2000) + private String text; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "event_id", nullable = false) + private Event event; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id", nullable = false) + private User author; + + @NotNull + private LocalDateTime created; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentDto.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentDto.java new file mode 100644 index 0000000..f645c44 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentDto.java @@ -0,0 +1,22 @@ +package ru.practicum.event.comment.model; + +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentDto { + private Long id; + + @Size(min = 1, max = 2000) + private String text; + private String authorName; + private LocalDateTime created; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentFullDto.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentFullDto.java new file mode 100644 index 0000000..a20b4ec --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/CommentFullDto.java @@ -0,0 +1,22 @@ +package ru.practicum.event.comment.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.event.model.EventShortDto; +import ru.practicum.user.model.UserShortDto; + +import java.time.LocalDateTime; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommentFullDto { + private Long id; + private String text; + private UserShortDto author; + private EventShortDto event; + private LocalDateTime created; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/NewCommentRequest.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/NewCommentRequest.java new file mode 100644 index 0000000..48f260c --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/NewCommentRequest.java @@ -0,0 +1,19 @@ +package ru.practicum.event.comment.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NewCommentRequest { + + @NotBlank + @Length(min = 1, max = 2000) + private String text; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/UpdateCommentRequest.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/UpdateCommentRequest.java new file mode 100644 index 0000000..beeca68 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/UpdateCommentRequest.java @@ -0,0 +1,19 @@ +package ru.practicum.event.comment.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UpdateCommentRequest { + + @NotBlank + @Length(min = 1, max = 2000) + private String text; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/model/UserCommentSearchRequest.java b/ewm-service/src/main/java/ru/practicum/event/comment/model/UserCommentSearchRequest.java new file mode 100644 index 0000000..063eb41 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/model/UserCommentSearchRequest.java @@ -0,0 +1,37 @@ +package ru.practicum.event.comment.model; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserCommentSearchRequest { + private List eventIds; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime rangeStart; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime rangeEnd; + + @PositiveOrZero + private int from = 0; + + @PositiveOrZero + private int size = 10; + + @AssertTrue(message = "Π”Π°Ρ‚Π° Π½Π°Ρ‡Π°Π»Π° Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΠΎΠ·ΠΆΠ΅ Π΄Π°Ρ‚Ρ‹ окончания") + public boolean isValidRange() { + return rangeStart == null || rangeEnd == null || !rangeStart.isAfter(rangeEnd); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepository.java b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepository.java new file mode 100644 index 0000000..617ad16 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepository.java @@ -0,0 +1,7 @@ +package ru.practicum.event.comment.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.event.comment.model.Comment; + +public interface CommentRepository extends JpaRepository, CommentRepositoryCustom { +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustom.java b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustom.java new file mode 100644 index 0000000..358699e --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustom.java @@ -0,0 +1,13 @@ +package ru.practicum.event.comment.repository; + +import ru.practicum.event.comment.model.AdminCommentSearchRequest; +import ru.practicum.event.comment.model.Comment; +import ru.practicum.event.comment.model.UserCommentSearchRequest; + +import java.util.List; + +public interface CommentRepositoryCustom { + List findByAdminFilter(AdminCommentSearchRequest request); + + List findByUserFilter(Long userId, UserCommentSearchRequest request); +} diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImpl.java b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImpl.java new file mode 100644 index 0000000..34ad5db --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImpl.java @@ -0,0 +1,84 @@ +package ru.practicum.event.comment.repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import ru.practicum.event.comment.model.AdminCommentSearchRequest; +import ru.practicum.event.comment.model.Comment; +import ru.practicum.event.comment.model.UserCommentSearchRequest; + +import java.util.ArrayList; +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class CommentRepositoryCustomImpl implements CommentRepositoryCustom { + private final EntityManager entityManager; + + @Override + public List findByAdminFilter(AdminCommentSearchRequest req) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Comment.class); + Root root = query.from(Comment.class); + + List predicates = new ArrayList<>(); + + if (req.getEventIds() != null && !req.getEventIds().isEmpty()) { + predicates.add(root.get("event").get("id").in(req.getEventIds())); + } + + if (req.getAuthorIds() != null && !req.getAuthorIds().isEmpty()) { + predicates.add(root.get("author").get("id").in(req.getAuthorIds())); + } + + if (req.getRangeStart() != null) { + predicates.add(cb.greaterThanOrEqualTo(root.get("created"), req.getRangeStart())); + } + + if (req.getRangeEnd() != null) { + predicates.add(cb.lessThanOrEqualTo(root.get("created"), req.getRangeEnd())); + } + + query.select(root) + .where(predicates.toArray(new Predicate[0])) + .orderBy(cb.desc(root.get("created"))); + + return entityManager.createQuery(query) + .setFirstResult(req.getFrom()) + .setMaxResults(req.getSize()) + .getResultList(); + } + + @Override + public List findByUserFilter(Long userId, UserCommentSearchRequest request) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Comment.class); + Root root = query.from(Comment.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("author").get("id"), userId)); + + if (request.getEventIds() != null && !request.getEventIds().isEmpty()) { + predicates.add(root.get("event").get("id").in(request.getEventIds())); + } + + if (request.getRangeStart() != null) { + predicates.add(cb.greaterThanOrEqualTo(root.get("created"), request.getRangeStart())); + } + if (request.getRangeEnd() != null) { + predicates.add(cb.lessThanOrEqualTo(root.get("created"), request.getRangeEnd())); + } + + query.where(cb.and(predicates.toArray(new Predicate[0]))); + query.orderBy(cb.desc(root.get("created"))); + + return entityManager.createQuery(query) + .setFirstResult(request.getFrom()) + .setMaxResults(request.getSize()) + .getResultList(); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentService.java b/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentService.java new file mode 100644 index 0000000..5641a14 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentService.java @@ -0,0 +1,22 @@ +package ru.practicum.event.comment.service; + +import ru.practicum.event.comment.model.*; + +import java.util.List; + +public interface CommentService { + + CommentDto addComment(Long userId, Long eventId, NewCommentRequest newCommentRequest); + + void deleteComment(Long userId, Long commentId); + + void deleteCommentByAdmin(Long commentId); + + CommentDto updateComment(Long userId, Long commentId, UpdateCommentRequest updateCommentRequest); + + Comment getEntityById(Long commentId); + + List getCommentsForModeration(AdminCommentSearchRequest adminCommentSearchRequest); + + List getOwnComments(Long userId, UserCommentSearchRequest userCommentSearchRequest); +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentServiceImpl.java b/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentServiceImpl.java new file mode 100644 index 0000000..4bee926 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/comment/service/CommentServiceImpl.java @@ -0,0 +1,113 @@ +package ru.practicum.event.comment.service; + +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.event.comment.mapper.CommentMapper; +import ru.practicum.event.comment.model.*; +import ru.practicum.event.comment.repository.CommentRepository; +import ru.practicum.event.model.Event; +import ru.practicum.event.model.state.EventState; +import ru.practicum.event.service.EventService; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.ForbiddenException; +import ru.practicum.exception.NotFoundException; +import ru.practicum.user.model.User; +import ru.practicum.user.service.UserService; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CommentServiceImpl implements CommentService { + private final CommentRepository commentRepository; + private final UserService userService; + private final EventService eventService; + private final CommentMapper commentMapper; + private final EntityManager entityManager; + + @Override + public CommentDto addComment(Long userId, Long eventId, NewCommentRequest newCommentRequest) { + User author = userService.getEntityById(userId); + Event event = eventService.getEntityById(eventId); + + if (!event.getState().equals(EventState.PUBLISHED)) { + throw new ConflictException("ΠžΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΌΠΎΠΆΠ½ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π½Ρ‹ΠΌ событиям."); + } + + Comment comment = Comment.builder() + .text(newCommentRequest.getText()) + .author(author) + .event(event) + .created(LocalDateTime.now()) + .build(); + + eventService.incrementCommentsCount(eventId); // ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ коммСнтария ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ счСтчик + commentRepository.save(comment); + + log.info("ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ {} Π΄ΠΎΠ±Π°Π²ΠΈΠ» ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ '{}'", userId, event.getTitle()); + return commentMapper.toDto(comment); + } + + @Override + public CommentDto updateComment(Long userId, Long commentId, UpdateCommentRequest updateCommentRequest) { + Comment existing = getEntityById(commentId); + userService.getEntityById(userId); + + if (!existing.getAuthor().getId().equals(userId)) { + throw new ForbiddenException("Волько Π°Π²Ρ‚ΠΎΡ€ коммСнтария ΠΌΠΎΠΆΠ΅Ρ‚ Π΅Π³ΠΎ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ."); + } + + commentMapper.update(updateCommentRequest, existing); + commentRepository.save(existing); + + log.info("ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ {} ΠΎΠ±Π½ΠΎΠ²ΠΈΠ» ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ '{}'", userId, existing.getEvent().getTitle()); + return commentMapper.toDto(existing); + } + + @Override + public void deleteComment(Long userId, Long commentId) { + Comment existing = getEntityById(commentId); + userService.getEntityById(userId); + + if (!existing.getAuthor().getId().equals(userId)) { + throw new ForbiddenException("Волько Π°Π²Ρ‚ΠΎΡ€ коммСнтария ΠΌΠΎΠΆΠ΅Ρ‚ Π΅Π³ΠΎ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ."); + } + + eventService.decrementCommentsCount(existing.getEvent().getId()); + commentRepository.deleteById(commentId); + log.info("ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ {} ΡƒΠ΄Π°Π»ΠΈΠ» ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ '{}'", userId, existing.getEvent().getTitle()); + } + + @Override + public void deleteCommentByAdmin(Long commentId) { + Comment existing = getEntityById(commentId); + commentRepository.deleteById(commentId); + log.info("ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΡƒΠ΄Π°Π»Π΅Π½ ΠΌΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΎΠΌ с id={} ΠΊ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ '{}'", commentId, existing.getEvent().getTitle()); + } + + @Override + public Comment getEntityById(Long commentId) { + return commentRepository.findById(commentId) + .orElseThrow(() -> new NotFoundException("ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ с id=" + commentId + " Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.")); + } + + @Override + public List getCommentsForModeration(AdminCommentSearchRequest request) { + List comments = commentRepository.findByAdminFilter(request); + return comments.stream() + .map(commentMapper::toFullDto) + .toList(); + } + + @Override + public List getOwnComments(Long userId, UserCommentSearchRequest userCommentSearchRequest) { + List comments = commentRepository.findByUserFilter(userId, userCommentSearchRequest); + return comments.stream() + .map(commentMapper::toFullDto) + .toList(); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/controller/PublicEventController.java b/ewm-service/src/main/java/ru/practicum/event/controller/PublicEventController.java index b3be848..2713768 100644 --- a/ewm-service/src/main/java/ru/practicum/event/controller/PublicEventController.java +++ b/ewm-service/src/main/java/ru/practicum/event/controller/PublicEventController.java @@ -20,8 +20,9 @@ public class PublicEventController { private final StatsClient statsClient; @GetMapping - public List getPublicEvents(@Valid @ModelAttribute PublicEventSearchRequest request, - HttpServletRequest httpRequest) { + public List getPublicEvents( + @Valid @ModelAttribute PublicEventSearchRequest request, + HttpServletRequest httpRequest) { return eventService.getPublicEvents(request, httpRequest); } diff --git a/ewm-service/src/main/java/ru/practicum/event/location/Location.java b/ewm-service/src/main/java/ru/practicum/event/location/Location.java index 9424ccf..81e2469 100644 --- a/ewm-service/src/main/java/ru/practicum/event/location/Location.java +++ b/ewm-service/src/main/java/ru/practicum/event/location/Location.java @@ -12,4 +12,4 @@ public class Location { private Double lat; private Double lon; -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/model/AdminEventSearchRequest.java b/ewm-service/src/main/java/ru/practicum/event/model/AdminEventSearchRequest.java index a3382d6..ff4c029 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/AdminEventSearchRequest.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/AdminEventSearchRequest.java @@ -30,4 +30,4 @@ public class AdminEventSearchRequest { @PositiveOrZero private int size = 10; -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/model/Event.java b/ewm-service/src/main/java/ru/practicum/event/model/Event.java index cce8be6..5454730 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/Event.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/Event.java @@ -78,4 +78,7 @@ public class Event { @Transient private int views; -} + + @Column(name = "comments_count", nullable = false) + private int commentsCount = 0; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/model/EventFullDto.java b/ewm-service/src/main/java/ru/practicum/event/model/EventFullDto.java index 9d6695a..d299c55 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/EventFullDto.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/EventFullDto.java @@ -42,4 +42,5 @@ public class EventFullDto { private EventState state; private String title; private int views; + private int commentsCount; } \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/model/EventShortDto.java b/ewm-service/src/main/java/ru/practicum/event/model/EventShortDto.java index 4cfb6b9..c9f66f2 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/EventShortDto.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/EventShortDto.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import ru.practicum.category.model.CategoryDto; @@ -12,6 +13,7 @@ @Data @AllArgsConstructor @NoArgsConstructor +@Builder public class EventShortDto { private String annotation; private CategoryDto category; @@ -27,4 +29,5 @@ public class EventShortDto { private Boolean requestModeration; private String title; private int views; -} + private int commentsCount; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/model/PublicEventSearchRequest.java b/ewm-service/src/main/java/ru/practicum/event/model/PublicEventSearchRequest.java index 77b6a54..8d6f958 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/PublicEventSearchRequest.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/PublicEventSearchRequest.java @@ -1,6 +1,7 @@ package ru.practicum.event.model; import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import lombok.Data; @@ -24,6 +25,8 @@ public class PublicEventSearchRequest { private LocalDateTime rangeEnd; private Boolean onlyAvailable = false; + + @Pattern(regexp = "EVENT_DATE|VIEWS|COMMENTS", message = "НСвСрный Ρ‚ΠΈΠΏ сортировки") private String sort = "EVENT_DATE"; @PositiveOrZero diff --git a/ewm-service/src/main/java/ru/practicum/event/model/state/EventState.java b/ewm-service/src/main/java/ru/practicum/event/model/state/EventState.java index b2560f1..8ca0995 100644 --- a/ewm-service/src/main/java/ru/practicum/event/model/state/EventState.java +++ b/ewm-service/src/main/java/ru/practicum/event/model/state/EventState.java @@ -4,4 +4,4 @@ public enum EventState { PENDING, PUBLISHED, CANCELED -} +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/repository/EventRepository.java b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepository.java index 5a488a4..5e88b82 100644 --- a/ewm-service/src/main/java/ru/practicum/event/repository/EventRepository.java +++ b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepository.java @@ -15,7 +15,8 @@ import java.util.Set; -public interface EventRepository extends JpaRepository, JpaSpecificationExecutor { +public interface EventRepository extends JpaRepository, JpaSpecificationExecutor, + EventRepositoryCustom { Page findAllByInitiatorId(Long userId, Pageable pageable); boolean existsByCategoryId(Long categoryId); @@ -63,4 +64,10 @@ Page findPublicEvents( Optional findByIdAndState(Long id, EventState state); Set findByIdIn(Set ids); + + @Query("SELECT e FROM Event e WHERE e.initiator.id = :userId ORDER BY e.id DESC LIMIT :size OFFSET :from") + List findUserEventsWithOffset(@Param("userId") Long userId, + @Param("from") int from, + @Param("size") int size); + } diff --git a/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustom.java b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustom.java new file mode 100644 index 0000000..78cdf7e --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustom.java @@ -0,0 +1,14 @@ +package ru.practicum.event.repository; + +import ru.practicum.event.model.AdminEventSearchRequest; +import ru.practicum.event.model.Event; +import ru.practicum.event.model.PublicEventSearchRequest; + +import java.util.List; + +public interface EventRepositoryCustom { + + List findPublicEventsByFilter(PublicEventSearchRequest request); + + List findByAdminFilter(AdminEventSearchRequest request); +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustomImpl.java b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustomImpl.java new file mode 100644 index 0000000..88dce7d --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/event/repository/EventRepositoryCustomImpl.java @@ -0,0 +1,120 @@ +package ru.practicum.event.repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import ru.practicum.event.model.AdminEventSearchRequest; +import ru.practicum.event.model.Event; +import ru.practicum.event.model.PublicEventSearchRequest; +import ru.practicum.event.model.state.EventState; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class EventRepositoryCustomImpl implements EventRepositoryCustom { + private final EntityManager entityManager; + + @Override + public List findPublicEventsByFilter(PublicEventSearchRequest request) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Event.class); + Root root = query.from(Event.class); + + List predicates = new ArrayList<>(); + + if (request.getText() != null) { + Predicate annotationLike = cb.like(cb.lower(root.get("annotation")), "%" + + request.getText().toLowerCase() + "%"); + Predicate descriptionLike = cb.like(cb.lower(root.get("description")), "%" + + request.getText().toLowerCase() + "%"); + predicates.add(cb.or(annotationLike, descriptionLike)); + } + + if (request.getCategories() != null && !request.getCategories().isEmpty()) { + predicates.add(root.get("category").get("id").in(request.getCategories())); + } + + if (request.getPaid() != null) { + predicates.add(cb.equal(root.get("paid"), request.getPaid())); + } + + predicates.add(cb.equal(root.get("state"), EventState.PUBLISHED)); + + LocalDateTime start = request.getRangeStart() != null ? request.getRangeStart() : LocalDateTime.now(); + LocalDateTime end = request.getRangeEnd() != null ? request.getRangeEnd() : LocalDateTime.now().plusYears(1); + predicates.add(cb.between(root.get("eventDate"), start, end)); + + if (Boolean.TRUE.equals(request.getOnlyAvailable())) { + predicates.add(cb.or( + cb.equal(root.get("participantLimit"), 0), + cb.greaterThan(root.get("participantLimit"), root.get("confirmedRequests")) + )); + } + + query.where(cb.and(predicates.toArray(new Predicate[0]))); + + // Π‘ΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²ΠΊΠ° + if ("COMMENTS".equals(request.getSort())) { + query.orderBy(cb.desc(root.get("commentsCount"))); + } else if ("VIEWS".equals(request.getSort())) { + query.orderBy(cb.desc(root.get("views"))); + } else { + query.orderBy(cb.desc(root.get("eventDate"))); + } + + // ΠŸΠ°Π³ΠΈΠ½Π°Ρ†ΠΈΡ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ + return entityManager.createQuery(query) + .setFirstResult(request.getFrom()) + .setMaxResults(request.getSize()) + .getResultList(); + } + + @Override + public List findByAdminFilter(AdminEventSearchRequest request) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Event.class); + Root root = query.from(Event.class); + + List predicates = new ArrayList<>(); + + if (request.getUsers() != null && !request.getUsers().isEmpty()) { + predicates.add(root.get("initiator").get("id").in(request.getUsers())); + } + + if (request.getStates() != null && !request.getStates().isEmpty()) { + List states = request.getStates().stream() + .map(EventState::valueOf) + .toList(); + predicates.add(root.get("state").in(states)); + } + + if (request.getCategories() != null && !request.getCategories().isEmpty()) { + predicates.add(root.get("category").get("id").in(request.getCategories())); + } + + LocalDateTime start = request.getRangeStart() != null + ? request.getRangeStart() + : LocalDateTime.now(); + + LocalDateTime end = request.getRangeEnd() != null + ? request.getRangeEnd() + : LocalDateTime.now().plusYears(1); + + predicates.add(cb.between(root.get("eventDate"), start, end)); + + query.where(cb.and(predicates.toArray(new Predicate[0]))); + query.orderBy(cb.desc(root.get("eventDate"))); // ΠΈΠ»ΠΈ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΊΡ€ΠΈΡ‚Π΅Ρ€ΠΈΠΉ сортировки + + return entityManager.createQuery(query) + .setFirstResult(request.getFrom()) + .setMaxResults(request.getSize()) + .getResultList(); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/service/EventService.java b/ewm-service/src/main/java/ru/practicum/event/service/EventService.java index d4d8f13..1e2ae8f 100644 --- a/ewm-service/src/main/java/ru/practicum/event/service/EventService.java +++ b/ewm-service/src/main/java/ru/practicum/event/service/EventService.java @@ -24,6 +24,10 @@ public interface EventService { void incrementConfirmedRequests(Long eventId, int count); + void decrementCommentsCount(Long eventId); + + void incrementCommentsCount(Long eventId); + Set findAllById(Set ids); List getEventsByParams(AdminEventSearchRequest params); @@ -31,5 +35,4 @@ public interface EventService { List getPublicEvents(PublicEventSearchRequest request, HttpServletRequest httpRequest); EventFullDto getPublishedEventById(Long eventId, HttpServletRequest httpRequest); - } \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java b/ewm-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java index 691bff6..ef35d04 100644 --- a/ewm-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java @@ -3,10 +3,6 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.practicum.StatsClient; @@ -151,9 +147,7 @@ public EventFullDto updateByAdmin(Long eventId, UpdateEventAdminRequest updateDt @Override public List getUserEvents(Long userId, int from, int size) { userService.getEntityById(userId); - - Pageable pageable = PageRequest.of(from / size, size, Sort.by("id").descending()); - List events = eventRepository.findAllByInitiatorId(userId, pageable).getContent(); + List events = eventRepository.findUserEventsWithOffset(userId, from, size); return events.stream() .map(eventMapper::toEventShortDto) @@ -198,6 +192,25 @@ public void incrementConfirmedRequests(Long eventId, int count) { log.info("ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½Π½Ρ‹Ρ… заявок стало большС. Π’Π΅ΠΏΠ΅Ρ€ΡŒ {}", event.getConfirmedRequests()); } + @Transactional + public void decrementCommentsCount(Long eventId) { + Event event = getEntityById(eventId); + if (event.getCommentsCount() > 0) { + event.setCommentsCount(event.getCommentsCount() - 1); + eventRepository.save(event); + log.info("ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² стало мСньшС. Π’Π΅ΠΏΠ΅Ρ€ΡŒ {}", event.getCommentsCount()); + } + } + + @Transactional + public void incrementCommentsCount(Long eventId) { + Event event = getEntityById(eventId); + event.setCommentsCount(event.getCommentsCount() + 1); + eventRepository.save(event); + log.info("ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² стало большС. Π’Π΅ΠΏΠ΅Ρ€ΡŒ {}", event.getCommentsCount()); + } + + @Override public Set findAllById(Set ids) { return eventRepository.findByIdIn(ids); @@ -205,38 +218,9 @@ public Set findAllById(Set ids) { @Override public List getEventsByParams(AdminEventSearchRequest request) { - Pageable pageable = PageRequest.of(request.getFrom() / request.getSize(), request.getSize()); - - List states = null; - if (request.getStates() != null) { - states = request.getStates().stream() - .map(s -> { - try { - return EventState.valueOf(s); - } catch (IllegalArgumentException e) { - throw new BadRequestException("НСкоррСктноС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ состояния: " + s); - } - }).toList(); - } + List events = eventRepository.findByAdminFilter(request); - LocalDateTime start = request.getRangeStart() != null - ? request.getRangeStart() - : LocalDateTime.now(); - - LocalDateTime end = request.getRangeEnd() != null - ? request.getRangeEnd() - : LocalDateTime.now().plusYears(1); - - Page page = eventRepository.findByAdminParams( - request.getUsers(), - states, - request.getCategories(), - start, - end, - pageable - ); - - return page.stream() + return events.stream() .map(eventMapper::toEventFullDto) .toList(); } @@ -245,36 +229,13 @@ public List getEventsByParams(AdminEventSearchRequest request) { public List getPublicEvents(PublicEventSearchRequest request, HttpServletRequest httpRequest) { sendStatistics(httpRequest); - Pageable pageable = PageRequest.of( - request.getFrom() / request.getSize(), - request.getSize(), - Sort.by(request.getSort().equals("VIEWS") ? "views" : "eventDate") - ); - - LocalDateTime start = request.getRangeStart() != null - ? request.getRangeStart() - : LocalDateTime.now(); - - LocalDateTime end = request.getRangeEnd() != null - ? request.getRangeEnd() - : LocalDateTime.now().plusYears(1); - - Page page = eventRepository.findPublicEvents( - request.getText(), - request.getCategories(), - request.getPaid(), - start, - end, - request.getOnlyAvailable() != null ? request.getOnlyAvailable() : false, - pageable - ); + List events = eventRepository.findPublicEventsByFilter(request); - return page.stream() + return events.stream() .map(eventMapper::toEventShortDto) .toList(); } - @Override public EventFullDto getPublishedEventById(Long eventId, HttpServletRequest httpRequest) { Event event = eventRepository.findByIdAndState(eventId, EventState.PUBLISHED) diff --git a/ewm-service/src/main/java/ru/practicum/user/repository/UserRepository.java b/ewm-service/src/main/java/ru/practicum/user/repository/UserRepository.java index 64cd07c..b8f8d9f 100644 --- a/ewm-service/src/main/java/ru/practicum/user/repository/UserRepository.java +++ b/ewm-service/src/main/java/ru/practicum/user/repository/UserRepository.java @@ -2,6 +2,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import ru.practicum.user.model.User; import java.util.List; @@ -13,4 +15,11 @@ public interface UserRepository extends JpaRepository { boolean existsById(Long id); boolean existsByEmail(String email); + + @Query("SELECT u FROM User u WHERE (:ids IS NULL OR u.id IN :ids) ORDER BY u.id LIMIT :size OFFSET :from") + List findUsersWithOffset( + @Param("ids") List ids, + @Param("from") int from, + @Param("size") int size + ); } \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java b/ewm-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java index 589107c..4144aaa 100644 --- a/ewm-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java +++ b/ewm-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java @@ -2,16 +2,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import ru.practicum.exception.ConflictException; import ru.practicum.exception.NotFoundException; import ru.practicum.user.mapper.UserMapper; -import ru.practicum.user.repository.UserRepository; import ru.practicum.user.model.NewUserRequest; import ru.practicum.user.model.User; import ru.practicum.user.model.UserDto; +import ru.practicum.user.repository.UserRepository; import java.util.List; @@ -44,14 +42,10 @@ public UserDto getUserById(Long userId) { @Override public List getUsers(List ids, int from, int size) { - Pageable pageable = PageRequest.of(from / size, size); + if (from < 0) throw new IllegalArgumentException("From parameter cannot be negative"); + if (size <= 0) throw new IllegalArgumentException("Size parameter must be positive"); - List users; - if (ids == null || ids.isEmpty()) { - users = userRepository.findAll(pageable).getContent(); - } else { - users = userRepository.findByIdIn(ids, pageable); - } + List users = userRepository.findUsersWithOffset(ids, from, size); return users.stream() .map(userMapper::toUserDto) diff --git a/ewm-service/src/main/resources/schema.sql b/ewm-service/src/main/resources/schema.sql index 0c8b723..258efed 100644 --- a/ewm-service/src/main/resources/schema.sql +++ b/ewm-service/src/main/resources/schema.sql @@ -25,7 +25,8 @@ CREATE TABLE IF NOT EXISTS events ( published_on TIMESTAMP, request_moderation BOOLEAN DEFAULT TRUE, state VARCHAR(32) NOT NULL, - title VARCHAR(120) NOT NULL + title VARCHAR(120) NOT NULL, + comments_count BIGINT NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS participation_requests ( @@ -47,4 +48,12 @@ CREATE TABLE IF NOT EXISTS compilation_events ( compilation_id BIGINT NOT NULL REFERENCES compilations(id) ON DELETE CASCADE, event_id BIGINT NOT NULL REFERENCES events(id) ON DELETE CASCADE, PRIMARY KEY (compilation_id, event_id) +); + +CREATE TABLE IF NOT EXISTS comments ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, + text TEXT NOT NULL, + event_id BIGINT NOT NULL REFERENCES events(id) ON DELETE CASCADE, + author_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created TIMESTAMP WITHOUT TIME ZONE NOT NULL ); \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/category/service/CategoryServiceImplTest.java b/ewm-service/src/test/java/ru/practicum/category/service/CategoryServiceImplTest.java index 44e1da0..27e232f 100644 --- a/ewm-service/src/test/java/ru/practicum/category/service/CategoryServiceImplTest.java +++ b/ewm-service/src/test/java/ru/practicum/category/service/CategoryServiceImplTest.java @@ -5,9 +5,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import ru.practicum.category.mapper.CategoryMapper; import ru.practicum.category.model.Category; import ru.practicum.category.model.CategoryDto; @@ -161,21 +158,25 @@ void getCategoryById_whenExists_shouldReturnDto() { @Test void getCategories_shouldReturnPaginatedResults() { - Pageable pageable = PageRequest.of(0, 10); + int from = 0; + int size = 10; List categories = List.of(category); - when(categoryRepository.findAll(pageable)).thenReturn(new PageImpl<>(categories)); + + when(categoryRepository.findWithOffset(from, size)).thenReturn(categories); when(categoryMapper.toCategoryDto(category)).thenReturn(categoryDto); - List result = categoryService.getCategories(0, 10); + List result = categoryService.getCategories(from, size); assertEquals(1, result.size()); assertEquals(categoryDto, result.get(0)); + + verify(categoryRepository).findWithOffset(from, size); + verify(categoryMapper).toCategoryDto(category); } @Test void getCategories_whenEmptyResult_shouldReturnEmptyList() { - Pageable pageable = PageRequest.of(0, 10); - when(categoryRepository.findAll(pageable)).thenReturn(new PageImpl<>(List.of())); + when(categoryRepository.findWithOffset(0, 10)).thenReturn(List.of()); List result = categoryService.getCategories(0, 10); @@ -183,12 +184,13 @@ void getCategories_whenEmptyResult_shouldReturnEmptyList() { } @Test - void getCategories_whenPageCalculated_shouldUseCorrectPage() { - Pageable expectedPageable = PageRequest.of(2, 5); // from=10, size=5 β†’ page=2 - when(categoryRepository.findAll(expectedPageable)).thenReturn(new PageImpl<>(List.of())); + void getCategories_whenCalled_shouldUseCorrectOffsetAndLimit() { + int from = 10; + int size = 5; - categoryService.getCategories(10, 5); + when(categoryRepository.findWithOffset(from, size)).thenReturn(List.of()); + categoryService.getCategories(from, size); - verify(categoryRepository).findAll(expectedPageable); + verify(categoryRepository).findWithOffset(from, size); } } \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/controller/AdminCommentControllerTest.java b/ewm-service/src/test/java/ru/practicum/event/comment/controller/AdminCommentControllerTest.java new file mode 100644 index 0000000..993b2d3 --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/controller/AdminCommentControllerTest.java @@ -0,0 +1,130 @@ +package ru.practicum.event.comment.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.event.comment.model.AdminCommentSearchRequest; +import ru.practicum.event.comment.model.CommentFullDto; +import ru.practicum.event.model.EventShortDto; +import ru.practicum.event.comment.service.CommentService; +import ru.practicum.user.model.UserShortDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(controllers = AdminCommentController.class) +class AdminCommentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private CommentService commentService; + + private UserShortDto createUserShortDto(Long id, String name) { + return UserShortDto.builder() + .id(id) + .name(name) + .build(); + } + + private EventShortDto createEventShortDto(Long id, String title) { + return EventShortDto.builder() + .id(id) + .title(title) + .build(); + } + + private CommentFullDto createCommentFullDto(Long id, String text, Long userId, String userName, + Long eventId, String eventTitle) { + return CommentFullDto.builder() + .id(id) + .text(text) + .author(createUserShortDto(userId, userName)) + .event(createEventShortDto(eventId, eventTitle)) + .created(LocalDateTime.now()) + .build(); + } + + @Test + void deleteComment_shouldReturn204() throws Exception { + mockMvc.perform(delete("/admin/comments/1")) + .andExpect(status().isNoContent()); + + Mockito.verify(commentService, Mockito.times(1)) + .deleteCommentByAdmin(anyLong()); + } + + @Test + void getCommentsForModeration_shouldReturnListOfComments() throws Exception { + CommentFullDto comment1 = createCommentFullDto( + 1L, "Text 1", 1L, "User 1", 1L, "Event 1"); + CommentFullDto comment2 = createCommentFullDto( + 2L, "Text 2", 2L, "User 2", 2L, "Event 2"); + + Mockito.when(commentService.getCommentsForModeration(any(AdminCommentSearchRequest.class))) + .thenReturn(List.of(comment1, comment2)); + + mockMvc.perform(get("/admin/comments") + .param("users", "1,2") + .param("events", "1,2") + .param("rangeStart", "2023-01-01 00:00:00") + .param("rangeEnd", "2023-12-31 23:59:59")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].id", is(comment1.getId()), Long.class)) + .andExpect(jsonPath("$[0].text", is(comment1.getText()))) + .andExpect(jsonPath("$[0].author.id", is(comment1.getAuthor().getId()), Long.class)) + .andExpect(jsonPath("$[0].event.id", is(comment1.getEvent().getId()), Long.class)) + .andExpect(jsonPath("$[1].id", is(comment2.getId()), Long.class)) + .andExpect(jsonPath("$[1].text", is(comment2.getText()))); + + Mockito.verify(commentService, Mockito.times(1)) + .getCommentsForModeration(any(AdminCommentSearchRequest.class)); + } + + @Test + void getCommentsForModeration_withInvalidParameters_shouldReturnBadRequest() throws Exception { + mockMvc.perform(get("/admin/comments") + .param("rangeStart", "invalid-date")) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/admin/comments") + .param("rangeStart", "2023-01-02 00:00:00") + .param("rangeEnd", "2023-01-01 23:59:59")) + .andExpect(status().isBadRequest()); + } + + @Test + void getCommentsForModeration_withEmptyParameters_shouldReturnAllComments() throws Exception { + CommentFullDto comment = createCommentFullDto( + 1L, "Text", 1L, "User", 1L, "Event"); + + Mockito.when(commentService.getCommentsForModeration(any(AdminCommentSearchRequest.class))) + .thenReturn(List.of(comment)); + + mockMvc.perform(get("/admin/comments")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))); + + Mockito.verify(commentService, Mockito.times(1)) + .getCommentsForModeration(any(AdminCommentSearchRequest.class)); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/controller/PrivateCommentControllerTest.java b/ewm-service/src/test/java/ru/practicum/event/comment/controller/PrivateCommentControllerTest.java new file mode 100644 index 0000000..ed74d90 --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/controller/PrivateCommentControllerTest.java @@ -0,0 +1,190 @@ +package ru.practicum.event.comment.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import ru.practicum.event.comment.model.*; +import ru.practicum.event.comment.service.CommentService; +import ru.practicum.event.model.EventShortDto; +import ru.practicum.user.model.UserShortDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(controllers = PrivateCommentController.class) +class PrivateCommentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private CommentService commentService; + + private UserShortDto createUserShortDto(Long id, String name) { + return UserShortDto.builder() + .id(id) + .name(name) + .build(); + } + + private EventShortDto createEventShortDto(Long id, String title) { + return EventShortDto.builder() + .id(id) + .title(title) + .build(); + } + + private CommentDto createCommentDto(Long id, String text) { + return CommentDto.builder() + .id(id) + .text(text) + .build(); + } + + private CommentFullDto createCommentFullDto(Long id, String text, Long userId, String userName, + Long eventId, String eventTitle) { + return CommentFullDto.builder() + .id(id) + .text(text) + .author(createUserShortDto(userId, userName)) + .event(createEventShortDto(eventId, eventTitle)) + .created(LocalDateTime.now()) + .build(); + } + + @Test + void addComment_shouldReturnCreatedComment() throws Exception { + NewCommentRequest request = new NewCommentRequest("Test comment text"); + CommentDto response = createCommentDto(1L, "Test comment text"); + + Mockito.when(commentService.addComment(anyLong(), anyLong(), any())) + .thenReturn(response); + + mockMvc.perform(post("/users/1/comments/events/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id", is(response.getId()), Long.class)) + .andExpect(jsonPath("$.text", is(response.getText()))); + + Mockito.verify(commentService, Mockito.times(1)) + .addComment(eq(1L), eq(1L), any()); + } + + @Test + void addComment_withInvalidText_shouldReturnBadRequest() throws Exception { + NewCommentRequest emptyTextRequest = new NewCommentRequest(""); + NewCommentRequest nullTextRequest = new NewCommentRequest(null); + NewCommentRequest tooLongTextRequest = new NewCommentRequest("a".repeat(2001)); + + mockMvc.perform(post("/users/1/comments/events/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(emptyTextRequest))) + .andExpect(status().isBadRequest()); + + mockMvc.perform(post("/users/1/comments/events/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(nullTextRequest))) + .andExpect(status().isBadRequest()); + + mockMvc.perform(post("/users/1/comments/events/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(tooLongTextRequest))) + .andExpect(status().isBadRequest()); + } + + @Test + void updateComment_shouldReturnUpdatedComment() throws Exception { + UpdateCommentRequest request = new UpdateCommentRequest("Updated text"); + CommentDto response = createCommentDto(1L, "Updated text"); + + Mockito.when(commentService.updateComment(anyLong(), anyLong(), any())) + .thenReturn(response); + + mockMvc.perform(patch("/users/1/comments/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(response.getId()), Long.class)) + .andExpect(jsonPath("$.text", is(response.getText()))); + + Mockito.verify(commentService, Mockito.times(1)) + .updateComment(eq(1L), eq(1L), any()); + } + + @Test + void deleteComment_shouldReturnNoContent() throws Exception { + mockMvc.perform(delete("/users/1/comments/1")) + .andExpect(status().isNoContent()); + + Mockito.verify(commentService, Mockito.times(1)) + .deleteComment(eq(1L), eq(1L)); + } + + @Test + void getOwnComments_shouldReturnListOfComments() throws Exception { + CommentFullDto comment1 = createCommentFullDto( + 1L, "Text 1", 1L, "User 1", 1L, "Event 1"); + CommentFullDto comment2 = createCommentFullDto( + 2L, "Text 2", 1L, "User 1", 2L, "Event 2"); + + Mockito.when(commentService.getOwnComments(anyLong(), any())) + .thenReturn(List.of(comment1, comment2)); + + mockMvc.perform(get("/users/1/comments") + .param("events", "1,2") + .param("rangeStart", "2023-01-01 00:00:00") + .param("rangeEnd", "2023-12-31 23:59:59")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].id", is(comment1.getId()), Long.class)) + .andExpect(jsonPath("$[0].text", is(comment1.getText()))) + .andExpect(jsonPath("$[1].id", is(comment2.getId()), Long.class)) + .andExpect(jsonPath("$[1].text", is(comment2.getText()))); + + Mockito.verify(commentService, Mockito.times(1)) + .getOwnComments(eq(1L), any()); + } + + @Test + void getOwnComments_withInvalidParameters_shouldReturnBadRequest() throws Exception { + mockMvc.perform(get("/users/1/comments") + .param("rangeStart", "invalid-date")) + .andExpect(status().isBadRequest()); + + mockMvc.perform(get("/users/1/comments") + .param("rangeStart", "2023-01-02 00:00:00") + .param("rangeEnd", "2023-01-01 23:59:59")) + .andExpect(status().isBadRequest()); + } + + @Test + void getOwnComments_withEmptyParameters_shouldReturnAllUserComments() throws Exception { + CommentFullDto comment = createCommentFullDto( + 1L, "Text", 1L, "User", 1L, "Event"); + + Mockito.when(commentService.getOwnComments(anyLong(), any())) + .thenReturn(List.of(comment)); + + mockMvc.perform(get("/users/1/comments")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))); + + Mockito.verify(commentService, Mockito.times(1)) + .getOwnComments(eq(1L), any()); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/mapper/CommentMapperTest.java b/ewm-service/src/test/java/ru/practicum/event/comment/mapper/CommentMapperTest.java new file mode 100644 index 0000000..dbc07ba --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/mapper/CommentMapperTest.java @@ -0,0 +1,133 @@ +package ru.practicum.event.comment.mapper; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.event.comment.model.*; +import ru.practicum.event.model.Event; +import ru.practicum.user.model.User; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class CommentMapperTest { + + @Autowired + private CommentMapper commentMapper; + + @Test + void toComment_shouldMapNewCommentRequestToComment() { + NewCommentRequest request = new NewCommentRequest("Test comment text"); + Comment comment = commentMapper.toComment(request); + + assertNotNull(comment); + assertEquals("Test comment text", comment.getText()); + assertNull(comment.getId()); + assertNull(comment.getAuthor()); + assertNull(comment.getEvent()); + } + + @Test + void toDto_shouldMapCommentToCommentDtoWithAuthorName() { + User author = User.builder() + .id(1L) + .name("Test User") + .build(); + + Comment comment = Comment.builder() + .id(1L) + .text("Test comment") + .author(author) + .created(LocalDateTime.now()) + .build(); + + CommentDto dto = commentMapper.toDto(comment); + + assertNotNull(dto); + assertEquals(1L, dto.getId()); + assertEquals("Test comment", dto.getText()); + assertEquals("Test User", dto.getAuthorName()); + } + + @Test + void update_shouldUpdateOnlyCommentText() { + Comment comment = Comment.builder() + .id(1L) + .text("Old text") + .author(User.builder().id(1L).build()) + .event(Event.builder().id(1L).build()) + .created(LocalDateTime.now()) + .build(); + + Comment updatedComment = commentMapper.update( + new UpdateCommentRequest("New text"), + comment + ); + + assertSame(comment, updatedComment); + assertEquals(1L, updatedComment.getId()); + assertEquals("New text", updatedComment.getText()); + assertNotNull(updatedComment.getAuthor()); + assertNotNull(updatedComment.getEvent()); + assertNotNull(updatedComment.getCreated()); + } + + @Test + void toFullDto_shouldMapAllFieldsCorrectly() { + User author = User.builder() + .id(1L) + .name("Test User") + .build(); + + Event event = Event.builder() + .id(1L) + .title("Test Event") + .build(); + + LocalDateTime created = LocalDateTime.now(); + + Comment comment = Comment.builder() + .id(1L) + .text("Full test comment") + .author(author) + .event(event) + .created(created) + .build(); + + CommentFullDto fullDto = commentMapper.toFullDto(comment); + + assertNotNull(fullDto); + assertEquals(1L, fullDto.getId()); + assertEquals("Full test comment", fullDto.getText()); + + assertNotNull(fullDto.getAuthor()); + assertEquals(1L, fullDto.getAuthor().getId()); + assertEquals("Test User", fullDto.getAuthor().getName()); + + assertNotNull(fullDto.getEvent()); + assertEquals(1L, fullDto.getEvent().getId()); + assertEquals("Test Event", fullDto.getEvent().getTitle()); + + assertEquals(created, fullDto.getCreated()); + } + + @Test + void toFullDto_withNullFields_shouldMapWithoutErrors() { + Comment comment = Comment.builder() + .id(1L) + .text("Comment with null fields") + .created(LocalDateTime.now()) + .build(); + + CommentFullDto fullDto = commentMapper.toFullDto(comment); + + assertNotNull(fullDto); + assertEquals(1L, fullDto.getId()); + assertEquals("Comment with null fields", fullDto.getText()); + assertNull(fullDto.getAuthor()); + assertNull(fullDto.getEvent()); + assertNotNull(fullDto.getCreated()); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImplTest.java b/ewm-service/src/test/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImplTest.java new file mode 100644 index 0000000..5d8bc71 --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/repository/CommentRepositoryCustomImplTest.java @@ -0,0 +1,102 @@ +package ru.practicum.event.comment.repository; + +import jakarta.persistence.EntityManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import ru.practicum.category.model.Category; +import ru.practicum.event.comment.model.AdminCommentSearchRequest; +import ru.practicum.event.comment.model.Comment; +import ru.practicum.event.comment.model.UserCommentSearchRequest; +import ru.practicum.event.location.Location; +import ru.practicum.event.model.Event; +import ru.practicum.user.model.User; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class CommentRepositoryCustomImplTest { + + @Autowired + private CommentRepositoryCustomImpl repository; + + @Autowired + private EntityManager em; + + private User user; + private Event event; + private Comment comment; + private Category category; + + @BeforeEach + void setup() { + user = User.builder() + .name("Author") + .email("author@test.com") + .build(); + em.persist(user); + + category = Category.builder() + .name("Movie") + .build(); + em.persist(category); + + event = Event.builder() + .title("Test Event") + .annotation("Odio sint delectus beatae nulla") + .description("Odio sint delectus beatae nulla") + .eventDate(LocalDateTime.now().plusDays(1)) + .createdOn(LocalDateTime.now()) + .initiator(user) + .paid(false) + .location(new Location(1.0, 1.0)) + .category(category) + .build(); + em.persist(event); + + comment = Comment.builder() + .author(user) + .event(event) + .text("Test comment") + .created(LocalDateTime.now()) + .build(); + em.persist(comment); + + em.flush(); + } + + @Test + void findByAdminFilter_shouldReturnComment() { + AdminCommentSearchRequest request = AdminCommentSearchRequest.builder() + .eventIds(List.of(event.getId())) + .authorIds(List.of(user.getId())) + .rangeStart(LocalDateTime.now().minusMinutes(5)) + .rangeEnd(LocalDateTime.now().plusMinutes(5)) + .from(0) + .size(10) + .build(); + + List result = repository.findByAdminFilter(request); + assertThat(result).hasSize(1); + assertThat(result.get(0).getText()).isEqualTo("Test comment"); + } + + @Test + void findByUserFilter_shouldReturnComment() { + UserCommentSearchRequest request = UserCommentSearchRequest.builder() + .eventIds(List.of(event.getId())) + .rangeStart(LocalDateTime.now().minusMinutes(5)) + .rangeEnd(LocalDateTime.now().plusMinutes(5)) + .from(0) + .size(10) + .build(); + + List result = repository.findByUserFilter(user.getId(), request); + assertThat(result).hasSize(1); + assertThat(result.get(0).getText()).isEqualTo("Test comment"); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplIT.java b/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplIT.java new file mode 100644 index 0000000..048b143 --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplIT.java @@ -0,0 +1,200 @@ +package ru.practicum.event.comment.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.category.model.Category; +import ru.practicum.category.repository.CategoryRepository; +import ru.practicum.event.comment.model.Comment; +import ru.practicum.event.comment.model.CommentDto; +import ru.practicum.event.comment.model.NewCommentRequest; +import ru.practicum.event.comment.model.UpdateCommentRequest; +import ru.practicum.event.comment.repository.CommentRepository; +import ru.practicum.event.location.Location; +import ru.practicum.event.model.Event; +import ru.practicum.event.model.state.EventState; +import ru.practicum.event.repository.EventRepository; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.ForbiddenException; +import ru.practicum.user.model.User; +import ru.practicum.user.repository.UserRepository; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +@AutoConfigureTestDatabase +class CommentServiceImplIT { + + @Autowired + private CommentServiceImpl commentService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private EventRepository eventRepository; + + @Autowired + private CommentRepository commentRepository; + + @Autowired + private CategoryRepository categoryRepository; + + private User author; + private Event publishedEvent; + private Category category; + + + @BeforeEach + void setUp() { + + author = userRepository.save(User.builder() + .email("author@example.com") + .name("Author") + .build()); + category = categoryRepository.save(new Category(null, "Concert")); + + + publishedEvent = eventRepository.save(Event.builder() + .title("Published Event") + .annotation("Odio sint delectus beatae nulla") + .description("Odio sint delectus beatae nulla") + .eventDate(LocalDateTime.now().plusDays(1)) + .location(new Location(55.75, 37.62)) + .paid(false) + .category(category) + .participantLimit(0) + .requestModeration(true) + .state(EventState.PUBLISHED) + .initiator(author) + .build()); + } + + @Test + void addComment_shouldSaveCommentWithAllFields() { + NewCommentRequest request = new NewCommentRequest("Test comment"); + + CommentDto result = commentService.addComment(author.getId(), publishedEvent.getId(), request); + + assertNotNull(result.getId()); + assertEquals("Test comment", result.getText()); + + Comment savedComment = commentRepository.findById(result.getId()).orElseThrow(); + assertEquals(author.getId(), savedComment.getAuthor().getId()); + assertEquals(publishedEvent.getId(), savedComment.getEvent().getId()); + assertNotNull(savedComment.getCreated()); + } + + @Test + void updateComment_shouldUpdateOnlyText() { + Comment comment = commentRepository.save(Comment.builder() + .text("Original text") + .author(author) + .event(publishedEvent) + .created(LocalDateTime.now()) + .build()); + + UpdateCommentRequest request = new UpdateCommentRequest("Updated text"); + CommentDto result = commentService.updateComment(author.getId(), comment.getId(), request); + + assertEquals("Updated text", result.getText()); + + Comment updatedComment = commentRepository.findById(comment.getId()).orElseThrow(); + assertEquals("Updated text", updatedComment.getText()); + assertEquals(comment.getCreated(), updatedComment.getCreated()); + assertEquals(comment.getAuthor().getId(), updatedComment.getAuthor().getId()); + assertEquals(comment.getEvent().getId(), updatedComment.getEvent().getId()); + } + + @Test + void deleteComment_shouldRemoveComment() { + Comment comment = commentRepository.save(Comment.builder() + .text("To be deleted") + .author(author) + .event(publishedEvent) + .created(LocalDateTime.now()) + .build()); + + commentService.deleteComment(author.getId(), comment.getId()); + + assertFalse(commentRepository.existsById(comment.getId())); + } + + @Test + void getEntityById_shouldReturnCorrectComment() { + Comment comment = commentRepository.save(Comment.builder() + .text("Test comment") + .author(author) + .event(publishedEvent) + .created(LocalDateTime.now()) + .build()); + + Comment result = commentService.getEntityById(comment.getId()); + + assertEquals(comment.getId(), result.getId()); + assertEquals(comment.getText(), result.getText()); + assertEquals(comment.getAuthor().getId(), result.getAuthor().getId()); + assertEquals(comment.getEvent().getId(), result.getEvent().getId()); + } + + @Test + void addComment_shouldFailForUnpublishedEvent() { + publishedEvent.setState(EventState.CANCELED); + + NewCommentRequest request = new NewCommentRequest("Test comment"); + + assertThrows(ConflictException.class, () -> + commentService.addComment(author.getId(), publishedEvent.getId(), request)); + + assertEquals(0, commentRepository.count()); + } + + @Test + void updateComment_shouldFailForNonAuthor() { + User otherUser = userRepository.save(User.builder() + .email("other@example.com") + .name("Other User") + .build()); + + Comment comment = commentRepository.save(Comment.builder() + .text("Original text") + .author(author) + .event(publishedEvent) + .created(LocalDateTime.now()) + .build()); + + UpdateCommentRequest request = new UpdateCommentRequest("Updated text"); + + assertThrows(ForbiddenException.class, () -> + commentService.updateComment(otherUser.getId(), comment.getId(), request)); + + Comment notUpdated = commentRepository.findById(comment.getId()).orElseThrow(); + assertEquals("Original text", notUpdated.getText()); + } + + @Test + void deleteComment_shouldFailForNonAuthor() { + User otherUser = userRepository.save(User.builder() + .email("other@example.com") + .name("Other User") + .build()); + + Comment comment = commentRepository.save(Comment.builder() + .text("To be deleted") + .author(author) + .event(publishedEvent) + .created(LocalDateTime.now()) + .build()); + + assertThrows(ForbiddenException.class, () -> + commentService.deleteComment(otherUser.getId(), comment.getId())); + + assertTrue(commentRepository.existsById(comment.getId())); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplTest.java b/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplTest.java new file mode 100644 index 0000000..87f7f62 --- /dev/null +++ b/ewm-service/src/test/java/ru/practicum/event/comment/service/CommentServiceImplTest.java @@ -0,0 +1,169 @@ +package ru.practicum.event.comment.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import ru.practicum.event.comment.mapper.CommentMapper; +import ru.practicum.event.comment.model.Comment; +import ru.practicum.event.comment.model.CommentDto; +import ru.practicum.event.comment.model.NewCommentRequest; +import ru.practicum.event.comment.model.UpdateCommentRequest; +import ru.practicum.event.comment.repository.CommentRepository; +import ru.practicum.event.model.Event; +import ru.practicum.event.model.state.EventState; +import ru.practicum.event.service.EventService; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.ForbiddenException; +import ru.practicum.exception.NotFoundException; +import ru.practicum.user.model.User; +import ru.practicum.user.service.UserService; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CommentServiceImplTest { + + @Mock + private CommentRepository commentRepository; + + @Mock + private UserService userService; + + @Mock + private EventService eventService; + + @Mock + private CommentMapper commentMapper; + + @InjectMocks + private CommentServiceImpl commentService; + + private User user; + private Event event; + private Comment comment; + private CommentDto commentDto; + + @BeforeEach + void setUp() { + user = new User(); + user.setId(1L); + user.setEmail("user@email.com"); + user.setName("User"); + + event = new Event(); + event.setId(1L); + event.setTitle("Event Title"); + event.setState(EventState.PUBLISHED); + + comment = Comment.builder() + .id(1L) + .text("Comment text") + .author(user) + .event(event) + .created(LocalDateTime.now()) + .build(); + + commentDto = new CommentDto(1L, "Comment text", "User", LocalDateTime.now()); + } + + @Test + void addComment_shouldCreateNewComment() { + NewCommentRequest request = new NewCommentRequest("New comment"); + when(userService.getEntityById(anyLong())).thenReturn(user); + when(eventService.getEntityById(anyLong())).thenReturn(event); + when(commentMapper.toDto(any(Comment.class))).thenReturn(commentDto); + when(commentRepository.save(any(Comment.class))).thenReturn(comment); + + CommentDto result = commentService.addComment(1L, 1L, request); + + assertNotNull(result); + assertEquals(commentDto.getId(), result.getId()); + verify(eventService).incrementCommentsCount(1L); + } + + @Test + void addComment_shouldThrowConflictForUnpublishedEvent() { + event.setState(EventState.PENDING); + NewCommentRequest request = new NewCommentRequest("New comment"); + when(userService.getEntityById(anyLong())).thenReturn(user); + when(eventService.getEntityById(anyLong())).thenReturn(event); + + assertThrows(ConflictException.class, () -> + commentService.addComment(1L, 1L, request)); + } + + @Test + void updateComment_shouldUpdateCommentText() { + UpdateCommentRequest request = new UpdateCommentRequest("Updated text"); + when(userService.getEntityById(anyLong())).thenReturn(user); + when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment)); + when(commentMapper.toDto(any(Comment.class))).thenReturn( + new CommentDto(1L, "Updated text", "User", LocalDateTime.now())); + + CommentDto result = commentService.updateComment(1L, 1L, request); + + assertNotNull(result); + assertEquals("Updated text", result.getText()); + verify(commentRepository).save(any(Comment.class)); + } + + @Test + void updateComment_shouldThrowForbiddenForNonAuthor() { + User otherUser = new User(); + otherUser.setId(2L); + UpdateCommentRequest request = new UpdateCommentRequest("Updated text"); + when(userService.getEntityById(anyLong())).thenReturn(otherUser); + when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment)); + + assertThrows(ForbiddenException.class, () -> + commentService.updateComment(2L, 1L, request)); + } + + @Test + void deleteComment_shouldDeleteComment() { + when(userService.getEntityById(anyLong())).thenReturn(user); + when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment)); + + commentService.deleteComment(1L, 1L); + + verify(commentRepository).deleteById(1L); + verify(eventService).decrementCommentsCount(1L); + } + + @Test + void deleteCommentByAdmin_shouldDeleteWithoutChecks() { + when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment)); + + commentService.deleteCommentByAdmin(1L); + + verify(commentRepository).deleteById(1L); + verify(eventService, never()).decrementCommentsCount(anyLong()); + } + + @Test + void getEntityById_shouldReturnComment() { + when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment)); + + Comment result = commentService.getEntityById(1L); + + assertNotNull(result); + assertEquals(comment.getId(), result.getId()); + } + + @Test + void getEntityById_shouldThrowNotFound() { + when(commentRepository.findById(anyLong())).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> + commentService.getEntityById(1L)); + } +} \ No newline at end of file diff --git a/ewm-service/src/test/java/ru/practicum/event/controller/PrivateEventControllerTest.java b/ewm-service/src/test/java/ru/practicum/event/controller/PrivateEventControllerTest.java index cdee445..e33f5f4 100644 --- a/ewm-service/src/test/java/ru/practicum/event/controller/PrivateEventControllerTest.java +++ b/ewm-service/src/test/java/ru/practicum/event/controller/PrivateEventControllerTest.java @@ -98,7 +98,7 @@ void getUserEvents_shouldReturnList() throws Exception { EventShortDto event = new EventShortDto("Annotation", new CategoryDto(), 0, LocalDateTime.now().plusDays(1), 5L, new UserShortDto(1L, null), null, null, - "Concert", 0); + "Concert", 0, 0); Mockito.when(eventService.getUserEvents(1L, 0, 10)).thenReturn(List.of(event)); mockMvc.perform(get("/users/1/events")) diff --git a/ewm-service/src/test/java/ru/practicum/event/service/EventServiceImplTest.java b/ewm-service/src/test/java/ru/practicum/event/service/EventServiceImplTest.java index f142fd3..21ef56b 100644 --- a/ewm-service/src/test/java/ru/practicum/event/service/EventServiceImplTest.java +++ b/ewm-service/src/test/java/ru/practicum/event/service/EventServiceImplTest.java @@ -6,7 +6,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.data.domain.PageImpl; import ru.practicum.StatsClient; import ru.practicum.category.model.Category; import ru.practicum.category.service.CategoryService; @@ -29,7 +28,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -196,15 +196,18 @@ void getUserEvents_shouldReturnMappedList() { Long userId = 1L; Event event = new Event(); EventShortDto dto = new EventShortDto(); + int from = 0; + int size = 10; when(userService.getEntityById(userId)).thenReturn(new User()); - when(eventRepository.findAllByInitiatorId(eq(userId), any())).thenReturn(new PageImpl<>(List.of(event))); + when(eventRepository.findUserEventsWithOffset(userId, from, size)).thenReturn(List.of(event)); when(eventMapper.toEventShortDto(event)).thenReturn(dto); - List result = eventService.getUserEvents(userId, 0, 10); + List result = eventService.getUserEvents(userId, from, size); assertThat(result).hasSize(1); assertThat(result.get(0)).isEqualTo(dto); + verify(eventRepository).findUserEventsWithOffset(userId, from, size); } @Test @@ -255,8 +258,8 @@ void getEventsByParams_shouldReturnMappedDtos() { Event e = new Event(); EventFullDto dto = new EventFullDto(); - when(eventRepository.findByAdminParams(any(), any(), any(), any(), any(), any())) - .thenReturn(new PageImpl<>(List.of(e))); + when(eventRepository.findByAdminFilter(any())) + .thenReturn(List.of(e)); when(eventMapper.toEventFullDto(e)).thenReturn(dto); List result = eventService.getEventsByParams(request); @@ -275,8 +278,10 @@ void getPublicEvents_shouldReturnMappedShortDtos() { Event e = new Event(); EventShortDto dto = new EventShortDto(); - when(eventRepository.findPublicEvents(any(), any(), any(), any(), any(), anyBoolean(), any())) - .thenReturn(new PageImpl<>(List.of(e))); +// when(eventRepository.findPublicEvents(any(), any(), any(), any(), any(), anyBoolean(), any())) +// .thenReturn(new PageImpl<>(List.of(e))); + when(eventRepository.findPublicEventsByFilter(any())).thenReturn(List.of(e)); + when(eventMapper.toEventShortDto(e)).thenReturn(dto); List result = eventService.getPublicEvents(r, request); diff --git a/ewm-service/src/test/java/ru/practicum/user/service/UserServiceImplTest.java b/ewm-service/src/test/java/ru/practicum/user/service/UserServiceImplTest.java index b4037de..b89421b 100644 --- a/ewm-service/src/test/java/ru/practicum/user/service/UserServiceImplTest.java +++ b/ewm-service/src/test/java/ru/practicum/user/service/UserServiceImplTest.java @@ -5,8 +5,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; import ru.practicum.exception.ConflictException; import ru.practicum.exception.NotFoundException; import ru.practicum.user.mapper.UserMapper; @@ -90,14 +88,16 @@ void getUserById_shouldThrowNotFound_whenMissing() { void getUsers_shouldReturnAllUsers_whenIdsNull() { User user = new User(1L, "mail@example.com", "User"); UserDto dto = new UserDto("mail@example.com", 1L, "User"); + int from = 0; + int size = 10; - when(userRepository.findAll(any(Pageable.class))) - .thenReturn(new PageImpl<>(List.of(user))); - + when(userRepository.findUsersWithOffset(null, from, size)).thenReturn(List.of(user)); when(userMapper.toUserDto(user)).thenReturn(dto); - List result = userService.getUsers(null, 0, 10); + List result = userService.getUsers(null, from, size); + assertThat(result).containsExactly(dto); + verify(userRepository).findUsersWithOffset(null, from, size); } @Test @@ -105,12 +105,16 @@ void getUsers_shouldReturnFilteredUsers_whenIdsProvided() { List ids = List.of(1L, 2L); User user1 = new User(1L, "a@a.com", "A"); UserDto dto1 = new UserDto("a@a.com", 1L, "A"); + int from = 0; + int size = 10; - when(userRepository.findByIdIn(eq(ids), any())).thenReturn(List.of(user1)); + when(userRepository.findUsersWithOffset(ids, from, size)).thenReturn(List.of(user1)); when(userMapper.toUserDto(user1)).thenReturn(dto1); - List result = userService.getUsers(ids, 0, 10); + List result = userService.getUsers(ids, from, size); + assertThat(result).containsExactly(dto1); + verify(userRepository).findUsersWithOffset(ids, from, size); } @Test diff --git a/postman/feature.json b/postman/feature.json new file mode 100644 index 0000000..31461c1 --- /dev/null +++ b/postman/feature.json @@ -0,0 +1,1962 @@ +{ + "info": { + "_postman_id": "0bbc53b8-5450-4a5f-b8fd-639ebc2ecb5b", + "name": "Test Explore With Me - Features_comments", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "42469278" + }, + "item": [ + { + "name": "Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Бтатус 201 Created\", function () {", + " pm.response.to.have.status(201);", + "});", + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ содСрТит поля id, text, created, authorName\", function () {", + " const json = pm.response.json();", + " pm.expect(json).to.have.property(\"id\");", + " pm.expect(json).to.have.property(\"text\");", + " pm.expect(json).to.have.property(\"created\");", + " pm.expect(json).to.have.property(\"authorName\");", + "", + "});", + "", + "let response = pm.response.json();", + "pm.collectionVariables.set(\"commentId\", response.id);", + "pm.collectionVariables.set(\"commentText\", response.text);", + "pm.collectionVariables.set(\"commentCreated\", response.created);", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\"text\":\"sssss\"}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π½ΠΎΠ΅ событиС", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " const user = await api.addUser(rnd.getUser());", + " const category = await api.addCategory(rnd.getCategory());", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));", + "", + " pm.collectionVariables.set(\"uid\", user.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 409 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(409);", + " pm.response.to.be.withBody;", + " pm.expect(pm.response.json().message).to.include(\"ΠžΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΌΠΎΠΆΠ½ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π½Ρ‹ΠΌ\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π½ΠΎΠ΅\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/events/{{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "events", + "{{eid}}" + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΡ коммСнтария ΠΎΡ‚ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 404 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(404);", + " pm.response.to.be.withBody;", + " pm.expect(pm.response.json().message).to.include(\"Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½\");", + "", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΎΡ‚ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/99999/comments/events/{{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "99999", + "comments", + "events", + "{{eid}}" + ] + } + }, + "response": [] + }, + { + "name": "ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΌΡƒ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 404 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(404);", + " pm.response.to.be.withBody;", + " pm.expect(pm.response.json().message).to.include(\"Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½\");", + "", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΊ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΌΡƒ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/events/99999", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "events", + "99999" + ] + } + }, + "response": [] + }, + { + "name": "НСвалидный ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/events/{{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "events", + "{{eid}}" + ] + } + }, + "response": [] + }, + { + "name": "text > 2000 символов", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.collectionVariables.set(\"longText\", 'a'.repeat(2001));" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"{{longText}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/events/{{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "events", + "{{eid}}" + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΡ коммСнтария ΠΎΡ‚ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ.", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 404 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(404);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΎΡ‚ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/99999/comments/events/{{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "99999", + "comments", + "events", + "{{eid}}" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ содСрТит поля id, text, created, authorName\", function () {", + " const json = pm.response.json();", + " pm.expect(json).to.have.property(\"id\");", + " pm.expect(json).to.have.property(\"text\");", + " pm.expect(json).to.have.property(\"created\");", + " pm.expect(json).to.have.property(\"authorName\");", + "", + "});", + "", + "pm.test(\"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Ρ‘Π½\", function () {", + " pm.response.to.have.status(200);", + " const response = pm.response.json();", + " pm.expect(response.text).to.eql(\"ΠžΠ±Π½ΠΎΠ²Π»Ρ‘Π½Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\");", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"ΠžΠ±Π½ΠΎΠ²Π»Ρ‘Π½Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:comentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":comentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "comentId", + "value": "{{commentId}}" + } + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° рСдактирования Ρ‡ΡƒΠΆΠΎΠ³ΠΎ коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " // Автор коммСнтария", + " const user1 = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"authorId\", user1.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + " let event = await api.addEvent(user1.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eventId\", event.id);", + "", + " // ДобавляСм ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΎΡ‚ user1", + " const comment = await api.post(`/users/${user1.id}/comments/events/${event.id}`, {", + " text: \"Π­Ρ‚ΠΎ Π½Π΅ Ρ‚Π²ΠΎΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + "", + " // Π’Ρ‚ΠΎΡ€ΠΎΠΉ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ попытаСтся Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ", + " const user2 = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"otherUserId\", user2.id);", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 403 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(403);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‡ΡƒΠΆΠΎΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{otherUserId}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{otherUserId}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ коммСнтария", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 404 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(404);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° обновлСния Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ коммСнтария\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/99999", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "99999" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС Π±Π΅Π· text", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{ }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС с text = null", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": null }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС с text = \"\"", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "ОбновлСниС с text > 2000 символов", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"text\": \"{{longText}}\" }", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ для удалСния\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 204\", function () {", + " pm.response.to.have.status(204);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:comentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":comentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "comentId", + "value": "{{commentId}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ коммСнтария Π°Π΄ΠΌΠΈΠ½ΠΎΠΌ", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΡƒΠ΄Π°Π»ΠΈΡ‚ Π°Π΄ΠΌΠΈΠ½\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + "", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"ΠœΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΡƒΠ΄Π°Π»ΠΈΠ» ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\", function () {", + " pm.response.to.have.status(204);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/admin/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 404 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(404);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/users/{{uid}}/comments/99999", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{uid}}", + "comments", + "99999" + ] + } + }, + "response": [] + }, + { + "name": "Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Ρ‡ΡƒΠΆΠΎΠ³ΠΎ коммСнтария", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " const user1 = await api.addUser(rnd.getUser());", + " const user2 = await api.addUser(rnd.getUser());", + "", + " pm.collectionVariables.set(\"user1Id\", user1.id);", + " pm.collectionVariables.set(\"user2Id\", user2.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + " let event = await api.addEvent(user1.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eventId\", event.id);", + "", + " const comment = await api.post(`/users/${user1.id}/comments/events/${event.id}`, {", + " text: \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΎΡ‚ ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ\"", + " });", + "", + " pm.collectionVariables.set(\"commentId\", comment.id);", + "};", + "", + "const interval = setInterval(() => {}, 1000); // Π·Π°Π³Π»ΡƒΡˆΠΊΠ°", + "", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ создании тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 403 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(403);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/users/{{otherUserId}}/comments/{{commentId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{otherUserId}}", + "comments", + "{{commentId}}" + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ собствСнных ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π²", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + "", + " await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"Π’Ρ‚ΠΎΡ€ΠΎΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ\"", + " });", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½Ρ‹ собствСнныС ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ\", function () {", + " pm.response.to.have.status(200);", + " const comments = pm.response.json();", + " pm.expect(comments.length).to.be.at.least(2);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ" + } + ] + } + }, + "response": [] + }, + { + "name": "ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² Π°Π΄ΠΌΠΈΠ½ΠΎΠΌ ΠΏΠΎ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°ΠΌ", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " try {", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + "", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ\"", + " });", + "", + " } catch (err) {", + " console.error(\"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…:\", err);", + " throw err;", + " }", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Админ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ\", function () {", + " pm.response.to.have.status(200);", + " const comments = pm.response.json();", + " pm.expect(comments.length).to.be.at.least(1);", + " pm.expect(comments[0].author.id).to.eql(+pm.collectionVariables.get(\"uid\"));", + " pm.expect(comments[0].event.id).to.eql(+pm.collectionVariables.get(\"eid\"));", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/comments?authorIds={{uid}}&eventIds={{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments" + ], + "query": [ + { + "key": "authorIds", + "value": "{{uid}}" + }, + { + "key": "eventIds", + "value": "{{eid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Π€ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΡ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² ΠΏΠΎ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Ρƒ Π΄Π°Ρ‚ (Π°Π΄ΠΌΠΈΠ½)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const api = new API(pm);", + "const rnd = new RandomUtils();", + "", + "const main = async () => {", + " // 1. Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈ событиС", + " const user = await api.addUser(rnd.getUser());", + " pm.collectionVariables.set(\"uid\", user.id);", + "", + " const category = await api.addCategory(rnd.getCategory());", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));", + " event = await api.publishEvent(event.id);", + " pm.collectionVariables.set(\"eid\", event.id);", + "", + " // 2. ДобавляСм ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π΅Π³ΠΎ created", + " const comment = await api.post(`/users/${user.id}/comments/events/${event.id}`, {", + " text: \"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ с Π΄Π°Ρ‚ΠΎΠΉ\"", + " });", + "", + " const created = comment.created; // <-- врСмя, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΠΎΠ΅ ΠΎΡ‚ сСрвСра", + " pm.collectionVariables.set(\"commentCreated\", created);", + "", + " // 3. УстанавливаСм Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½: -1 ΠΌΠΈΠ½ΡƒΡ‚Π° ΠΈ +1 ΠΌΠΈΠ½ΡƒΡ‚Π° ΠΎΡ‚ created", + " const moment = require(\"moment\");", + " const start = moment(created, \"YYYY-MM-DD HH:mm:ss\").subtract(1, \"minute\").format(\"YYYY-MM-DD HH:mm:ss\");", + " const end = moment(created, \"YYYY-MM-DD HH:mm:ss\").add(1, \"minute\").format(\"YYYY-MM-DD HH:mm:ss\");", + "", + " pm.collectionVariables.set(\"rangeStart\", start);", + " pm.collectionVariables.set(\"rangeEnd\", end);", + "};", + "", + "const interval = setInterval(() => {}, 1000);", + "setTimeout(async () => {", + " try {", + " await main();", + " } catch (e) {", + " console.error(e);", + " } finally {", + " clearInterval(interval);", + " }", + "}, 100);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ Π½Π°ΠΉΠ΄Π΅Π½ ΠΏΠΎ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Ρƒ Π΄Π°Ρ‚\", function () {", + " const json = pm.response.json();", + " pm.response.to.have.status(200);", + " pm.expect(json.length).to.be.above(0);", + " pm.expect(json[0].author.id).to.eql(+pm.collectionVariables.get(\"uid\"));", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/admin/comments?rangeStart={{rangeStart}}&rangeEnd={{rangeEnd}}&authorIds={{uid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments" + ], + "query": [ + { + "key": "rangeStart", + "value": "{{rangeStart}}" + }, + { + "key": "rangeEnd", + "value": "{{rangeEnd}}" + }, + { + "key": "authorIds", + "value": "{{uid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Запрос ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² с rangeStart > rangeEnd", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "", + "pm.test(\"ΠžΡ‚Π²Π΅Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ статуса 400 ΠΈ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ json\", function () {", + " pm.response.to.have.status(400);", + " pm.response.to.be.withBody;", + "", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/comments?rangeStart=2025-12-31 23:59:59&rangeEnd=2025-01-01 00:00:00", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments" + ], + "query": [ + { + "key": "rangeStart", + "value": "2025-12-31 23:59:59" + }, + { + "key": "rangeEnd", + "value": "2025-01-01 00:00:00" + } + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "API = class {", + " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {", + " this.baseUrl = baseUrl;", + " this.pm = postman;", + " this._verbose = verbose;", + " }", + "", + " async addUser(user, verbose=null) {", + " return this.post(\"/admin/users\", user, \"Ошибка ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ: \", verbose);", + " }", + "", + " async addCategory(category, verbose=null) {", + " return this.post(\"/admin/categories\", category, \"Ошибка ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠΉ ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠΈ: \", verbose);", + " }", + "", + " async addEvent(userId, event, verbose=null) {", + " return this.post(\"/users/\" + userId + \"/events\", event, \"Ошибка ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ события: \", verbose);", + " }", + "", + " async addCompilation(compilation, verbose=null) {", + " return this.post(\"/admin/compilations\", compilation, \"Ошибка ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠΉ ΠΏΠΎΠ΄Π±ΠΎΡ€ΠΊΠΈ: \", verbose);", + " }", + "", + " async publishParticipationRequest(eventId, userId, verbose=null) {", + " return this.post('/users/' + userId + '/requests?eventId=' + eventId, null, \"Ошибка ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ запроса Π½Π° участиС Π² событии\", verbose);", + " }", + "", + " async publishEvent(eventId, verbose=null) {", + " return this.patch('/admin/events/' + eventId, {stateAction: \"PUBLISH_EVENT\"}, \"Ошибка ΠΏΡ€ΠΈ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ события\", verbose);", + " }", + " ", + " async rejectEvent(eventId, verbose=null) {", + " return this.patch('/admin/events/' + eventId, {stateAction: \"REJECT_EVENT\"}, \"Ошибка ΠΏΡ€ΠΈ ΠΎΡ‚ΠΌΠ΅Π½Π΅ события\", verbose);", + " }", + "", + " async acceptParticipationRequest(eventId, userId, reqId, verbose=null) {", + " return this.patch('/users/' + userId + '/events/' + eventId + '/requests', {requestIds:[reqId], status: \"CONFIRMED\"}, \"Ошибка ΠΏΡ€ΠΈ принятии заявки Π½Π° участиС Π² событии\", verbose);", + " }", + "", + " async findCategory(catId, verbose=null) {", + " return this.get('/categories/' + catId, null, \"Ошибка ΠΏΡ€ΠΈ поискС ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠΈ ΠΏΠΎ id\", verbose);", + " }", + "", + " async findCompilation(compId, verbose=null) {", + " return this.get('/compilations/' + compId, null, \"Ошибка ΠΏΡ€ΠΈ поискС ΠΏΠΎΠ΄Π±ΠΎΡ€ΠΊΠΈ ΠΏΠΎ id\", verbose);", + " }", + "", + " async findEvent(eventId, verbose=null) {", + " return this.get('/events/' + eventId, null, \"Ошибка ΠΏΡ€ΠΈ поискС события ΠΏΠΎ id\", verbose);", + " }", + "", + " async findUser(userId, verbose=null) {", + " return this.get('/admin/users?ids=' + userId, null, \"Ошибка ΠΏΡ€ΠΈ поискС ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΏΠΎ id\", verbose);", + " }", + "", + " async post(path, body, errorText = \"Ошибка ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ post-запроса: \", verbose=null) {", + " return this.sendRequest(\"POST\", path, body, errorText, verbose);", + " }", + "", + " async patch(path, body = null, errorText = \"Ошибка ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ patch-запроса: \", verbose=null) {", + " return this.sendRequest(\"PATCH\", path, body, errorText, verbose);", + " }", + "", + " async get(path, body = null, errorText = \"Ошибка ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ get-запроса: \", verbose=null) {", + " return this.sendRequest(\"GET\", path, body, errorText, verbose);", + " }", + " async sendRequest(method, path, body=null, errorText = \"Ошибка ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ запроса: \", verbose=null) {", + " return new Promise((resolve, reject) => {", + " verbose = verbose == null ? this._verbose : verbose;", + " const request = {", + " url: this.baseUrl + path,", + " method: method,", + " body: body == null ? \"\" : JSON.stringify(body),", + " header: { \"Content-Type\": \"application/json\" },", + " };", + " if(verbose) {", + " console.log(\"ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΡŽ запрос: \", request);", + " }", + "", + " try {", + " this.pm.sendRequest(request, (error, response) => {", + " if(error || (response.code >= 400 && response.code <= 599)) {", + " let err = error ? error : JSON.stringify(response.json());", + " console.error(\"ΠŸΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ запроса ΠΊ сСрвСру Π²ΠΎΠ·Π½ΠΈΠΊΠ»Π° ошика.\\n\", err,", + " \"\\nДля ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚Π΅ Ρ‚Π°ΠΊΠΎΠΉ ΠΆΠ΅ запрос ΠΊ вашСй ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅ \" + ", + " \"Π½Π° локальном ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π΅. Π”Π°Π½Π½Ρ‹Π΅ запроса:\\n\", JSON.stringify(request));", + "", + " reject(new Error(errorText + err));", + " }", + " if(verbose) {", + " console.log(\"Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ запроса: ΠΊΠΎΠ΄ состояния - \", response.code, \", Ρ‚Π΅Π»ΠΎ: \", response.json());", + " }", + " if (response.stream.length === 0){", + " reject(new Error('ΠžΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½ΠΎ пустоС Ρ‚Π΅Π»ΠΎ ΠΎΡ‚Π²Π΅Ρ‚Π°'))", + " }else{", + " resolve(response.json());", + " }", + " });", + " ", + " } catch(err) {", + " if(verbose) {", + " console.error(errorText, err);", + " }", + " return Promise.reject(err);", + " }", + " });", + " }", + "};", + "", + "RandomUtils = class {", + " constructor() {}", + "", + " getUser() {", + " return {", + " name: pm.variables.replaceIn('{{$randomFullName}}'),", + " email: pm.variables.replaceIn('{{$randomEmail}}')", + " };", + " }", + "", + " getCategory() {", + " return {", + " name: pm.variables.replaceIn('{{$randomWord}}') + Math.floor(Math.random() * 10000 * Math.random()).toString()", + " };", + " }", + "", + " getEvent(categoryId) {", + " return {", + " annotation: pm.variables.replaceIn('{{$randomLoremParagraph}}'),", + " category: categoryId,", + " description: pm.variables.replaceIn('{{$randomLoremParagraphs}}'),", + " eventDate: this.getFutureDateTime(),", + " location: {", + " lat: parseFloat(pm.variables.replaceIn('{{$randomLatitude}}')),", + " lon: parseFloat(pm.variables.replaceIn('{{$randomLongitude}}')),", + " },", + " paid: pm.variables.replaceIn('{{$randomBoolean}}'),", + " participantLimit: pm.variables.replaceIn('{{$randomInt}}'),", + " requestModeration: pm.variables.replaceIn('{{$randomBoolean}}'),", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}'),", + " }", + " }", + "", + " getCompilation(...eventIds) {", + " return {", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}').slice(0, 50),", + " pinned: pm.variables.replaceIn('{{$randomBoolean}}'),", + " events: eventIds", + " };", + " }", + "", + "", + " getFutureDateTime(hourShift = 5, minuteShift=0, yearShift=0) {", + " let moment = require('moment');", + "", + " let m = moment();", + " m.add(hourShift, 'hour');", + " m.add(minuteShift, 'minute');", + " m.add(yearShift, 'year');", + "", + " return m.format('YYYY-MM-DD HH:mm:ss');", + " }", + "", + " getWord(length = 1) {", + " let result = '';", + " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';", + " const charactersLength = characters.length;", + " let counter = 0;", + " while (counter < length) {", + " result += characters.charAt(Math.floor(Math.random() * charactersLength));", + " counter += 1;", + " }", + " return result;", + " }", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080", + "type": "string" + }, + { + "key": "uid", + "value": "" + }, + { + "key": "userId", + "value": "" + }, + { + "key": "eventId", + "value": "" + }, + { + "key": "eid", + "value": "" + }, + { + "key": "response", + "value": "" + }, + { + "key": "cid", + "value": "" + }, + { + "key": "commentId", + "value": "" + }, + { + "key": "commentText", + "value": "" + }, + { + "key": "commentCreated", + "value": "" + }, + { + "key": "rangeStart", + "value": "" + }, + { + "key": "rangeEnd", + "value": "" + }, + { + "key": "authorId", + "value": "" + }, + { + "key": "otherUserId", + "value": "" + }, + { + "key": "longText", + "value": "" + }, + { + "key": "user1Id", + "value": "" + }, + { + "key": "user2Id", + "value": "" + } + ] +} \ No newline at end of file