diff --git a/src/main/java/com/chat/yourway/controller/TopicController.java b/src/main/java/com/chat/yourway/controller/TopicController.java index 5bc7f297..ad7f6117 100644 --- a/src/main/java/com/chat/yourway/controller/TopicController.java +++ b/src/main/java/com/chat/yourway/controller/TopicController.java @@ -8,6 +8,7 @@ import com.chat.yourway.dto.request.TopicRequestDto; import com.chat.yourway.dto.response.ApiErrorResponseDto; import com.chat.yourway.dto.response.ContactResponseDto; +import com.chat.yourway.dto.response.TopicInfoResponseDto; import com.chat.yourway.dto.response.TopicResponseDto; import com.chat.yourway.service.interfaces.TopicService; import com.chat.yourway.service.interfaces.TopicSubscriberService; @@ -149,7 +150,7 @@ public TopicResponseDto findById(@PathVariable Integer id) { content = @Content(schema = @Schema(implementation = ApiErrorResponseDto.class))) }) @GetMapping(path = "/all", produces = APPLICATION_JSON_VALUE) - public List findAllPublic() { + public List findAllPublic() { return topicService.findAllPublic(); } @@ -322,7 +323,7 @@ public void removeToFavouriteTopic( content = @Content(schema = @Schema(implementation = ApiErrorResponseDto.class))) }) @GetMapping(path = "/favourite", produces = APPLICATION_JSON_VALUE) - public List findAllFavouriteTopics( + public List findAllFavouriteTopics( @AuthenticationPrincipal UserDetails userDetails) { return topicService.findAllFavouriteTopics(userDetails); } @@ -333,7 +334,7 @@ public List findAllFavouriteTopics( @ApiResponse(responseCode = "200", description = SUCCESSFULLY_FOUND_TOPIC) }) @GetMapping(path = "/popular/public", produces = APPLICATION_JSON_VALUE) - public List findAllPopularPublicTopics() { + public List findAllPopularPublicTopics() { return topicService.findPopularPublicTopics(); } diff --git a/src/main/java/com/chat/yourway/dto/response/TopicInfoResponseDto.java b/src/main/java/com/chat/yourway/dto/response/TopicInfoResponseDto.java new file mode 100644 index 00000000..d1dec5db --- /dev/null +++ b/src/main/java/com/chat/yourway/dto/response/TopicInfoResponseDto.java @@ -0,0 +1,26 @@ +package com.chat.yourway.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class TopicInfoResponseDto { + @Schema(description = "ID", example = "1") + private Integer id; + @Schema(description = "New Topic name", example = "My programming topic") + private String topicName; + @Schema(description = "Email of Topic creator", example = "example@gmail.com") + private String createdBy; + @Schema(description = "Created time") + private LocalDateTime createdAt; + +} diff --git a/src/main/java/com/chat/yourway/mapper/TopicMapper.java b/src/main/java/com/chat/yourway/mapper/TopicMapper.java index 0d71fb5e..2dff6d6e 100644 --- a/src/main/java/com/chat/yourway/mapper/TopicMapper.java +++ b/src/main/java/com/chat/yourway/mapper/TopicMapper.java @@ -1,5 +1,6 @@ package com.chat.yourway.mapper; +import com.chat.yourway.dto.response.TopicInfoResponseDto; import com.chat.yourway.dto.response.TopicResponseDto; import com.chat.yourway.model.Topic; import java.util.List; @@ -12,6 +13,8 @@ public interface TopicMapper { TopicResponseDto toResponseDto(Topic topic); List toListResponseDto(List topics); + List toListInfoResponseDto(List topics); + @Mapping(target = "messages", ignore = true) Topic toEntity(TopicResponseDto topicResponseDto); diff --git a/src/main/java/com/chat/yourway/model/Topic.java b/src/main/java/com/chat/yourway/model/Topic.java index 0eca84d3..1e05c306 100644 --- a/src/main/java/com/chat/yourway/model/Topic.java +++ b/src/main/java/com/chat/yourway/model/Topic.java @@ -48,7 +48,7 @@ public class Topic { @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; - @ManyToMany(fetch = FetchType.EAGER) + @ManyToMany(fetch = FetchType.LAZY) @JoinTable( schema = "chat", name = "topic_tag", @@ -56,7 +56,7 @@ public class Topic { inverseJoinColumns = @JoinColumn(name = "tag_id")) private Set tags; - @OneToMany(mappedBy = "topic", fetch = FetchType.EAGER, cascade = CascadeType.ALL) + @OneToMany(mappedBy = "topic", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private Set topicSubscribers; @OneToMany(mappedBy = "topic", fetch = FetchType.LAZY, cascade = CascadeType.ALL) diff --git a/src/main/java/com/chat/yourway/repository/TopicRepository.java b/src/main/java/com/chat/yourway/repository/TopicRepository.java index 5c0a0b53..de34ae69 100644 --- a/src/main/java/com/chat/yourway/repository/TopicRepository.java +++ b/src/main/java/com/chat/yourway/repository/TopicRepository.java @@ -18,10 +18,10 @@ public interface TopicRepository extends JpaRepository { @Query( value = """ - SELECT * - FROM chat.topic t - WHERE to_tsvector('english', t.topic_name) @@ to_tsquery('english', :query) - """, + SELECT * + FROM chat.topic t + WHERE to_tsvector('english', t.topic_name) @@ to_tsquery('english', :query) + """, nativeQuery = true) List findAllByTopicName(String query); @@ -31,18 +31,16 @@ WHERE to_tsvector('english', t.topic_name) @@ to_tsquery('english', :query) @Query( "select t from Topic t join fetch t.topicSubscribers ts " - + "where ts.contact.email = :contactEmail and ts.isFavouriteTopic = true") + + "where ts.contact.email = :contactEmail and ts.isFavouriteTopic = true") List findAllFavouriteTopicsByContactEmail(String contactEmail); - boolean existsByIdAndIsPublic(int topicId, boolean isPublic); - @Query(nativeQuery = true, value = - "SELECT t.*, COUNT(ts.id) AS ts_count, COUNT(m.id) AS m_count " + - "FROM chat.topic t " + - "JOIN chat.topic_subscriber ts ON t.id = ts.topic_id " + - "JOIN chat.message m ON t.id = m.topic_id " + - "WHERE t.is_public = true " + - "GROUP BY t.id " + - "ORDER BY ts_count DESC, m_count DESC") + "SELECT t.*, COUNT(ts.id) AS ts_count, COUNT(m.id) AS m_count " + + "FROM chat.topic t " + + "JOIN chat.topic_subscriber ts ON t.id = ts.topic_id " + + "JOIN chat.message m ON t.id = m.topic_id " + + "WHERE t.is_public = true " + + "GROUP BY t.id " + + "ORDER BY ts_count DESC, m_count DESC") List findPopularPublicTopics(); } diff --git a/src/main/java/com/chat/yourway/service/TopicServiceImpl.java b/src/main/java/com/chat/yourway/service/TopicServiceImpl.java index 60ad4913..2470e70f 100644 --- a/src/main/java/com/chat/yourway/service/TopicServiceImpl.java +++ b/src/main/java/com/chat/yourway/service/TopicServiceImpl.java @@ -5,6 +5,7 @@ import com.chat.yourway.dto.request.TagRequestDto; import com.chat.yourway.dto.request.TopicPrivateRequestDto; import com.chat.yourway.dto.request.TopicRequestDto; +import com.chat.yourway.dto.response.TopicInfoResponseDto; import com.chat.yourway.dto.response.TopicResponseDto; import com.chat.yourway.exception.ContactEmailNotExist; import com.chat.yourway.exception.TopicAccessException; @@ -100,13 +101,13 @@ public TopicResponseDto findByName(String name) { } @Override - public List findAllPublic() { + public List findAllPublic() { log.trace("Started findAllPublic"); List topics = topicRepository.findAllByIsPublicIsTrue(); log.trace("All public topics was found"); - return topicMapper.toListResponseDto(topics); + return topicMapper.toListInfoResponseDto(topics); } @Override @@ -173,15 +174,15 @@ public String generatePrivateName(String sendTo, String email) { } @Override - public List findAllFavouriteTopics(UserDetails userDetails) { + public List findAllFavouriteTopics(UserDetails userDetails) { String contactEmail = userDetails.getUsername(); - return topicMapper.toListResponseDto( + return topicMapper.toListInfoResponseDto( topicRepository.findAllFavouriteTopicsByContactEmail(contactEmail)); } @Override - public List findPopularPublicTopics() { - return topicMapper.toListResponseDto(topicRepository.findPopularPublicTopics()); + public List findPopularPublicTopics() { + return topicMapper.toListInfoResponseDto(topicRepository.findPopularPublicTopics()); } private Topic createOrUpdateTopic(Topic topic, TopicRequestDto topicRequestDto, String email) { diff --git a/src/main/java/com/chat/yourway/service/interfaces/TopicService.java b/src/main/java/com/chat/yourway/service/interfaces/TopicService.java index 41e1c974..3e305d8a 100644 --- a/src/main/java/com/chat/yourway/service/interfaces/TopicService.java +++ b/src/main/java/com/chat/yourway/service/interfaces/TopicService.java @@ -3,16 +3,15 @@ import com.chat.yourway.dto.request.TagRequestDto; import com.chat.yourway.dto.request.TopicPrivateRequestDto; import com.chat.yourway.dto.request.TopicRequestDto; +import com.chat.yourway.dto.response.TopicInfoResponseDto; import com.chat.yourway.dto.response.TopicResponseDto; import com.chat.yourway.exception.TopicAccessException; import com.chat.yourway.exception.TopicNotFoundException; import com.chat.yourway.exception.ValueNotUniqException; import com.chat.yourway.model.Tag; -import com.chat.yourway.model.Topic; -import org.springframework.security.core.userdetails.UserDetails; - import java.util.List; import java.util.Set; +import org.springframework.security.core.userdetails.UserDetails; public interface TopicService { @@ -20,7 +19,7 @@ public interface TopicService { * Creates a new topic with the specified email of the creator. * * @param topicRequestDto Request object for creating topic. - * @param email The email of the creator. + * @param email The email of the creator. * @return Created topic. * @throws ValueNotUniqException If the topic name already in use. */ @@ -31,7 +30,7 @@ public interface TopicService { * contacts. * * @param topicPrivateDto Request object for creating topic. - * @param email The email of the creator. + * @param email The email of the creator. * @return Created private topic. * @throws ValueNotUniqException If the topic name already in use. */ @@ -40,12 +39,12 @@ public interface TopicService { /** * Update an existing topic with the specified email of the creator. * - * @param topicId The ID of the topic to find. + * @param topicId The ID of the topic to find. * @param topicRequestDto Request object for creating topic. - * @param email The email of the creator. + * @param email The email of the creator. * @return Updated topic. * @throws ValueNotUniqException If the topic name already in use. - * @throws TopicAccessException if the email is not the creator of the topic. + * @throws TopicAccessException if the email is not the creator of the topic. */ TopicResponseDto update(Integer topicId, TopicRequestDto topicRequestDto, String email); @@ -72,12 +71,12 @@ public interface TopicService { * * @return A list of public topics. */ - List findAllPublic(); + List findAllPublic(); /** * Deletes a topic by ID if the specified email is the creator of the topic. * - * @param id The ID of the topic to delete. + * @param id The ID of the topic to delete. * @param email The email of the user. * @throws TopicAccessException if the email is not the creator of the topic. */ @@ -88,7 +87,7 @@ public interface TopicService { * * @param tagName The unique name of the tag for which topics are to be retrieved. * @return A list of {@link TopicResponseDto} objects associated with the given tag. An empty list - * is returned if no topics are found for the specified tag. + * is returned if no topics are found for the specified tag. */ List findTopicsByTagName(String tagName); @@ -116,7 +115,7 @@ public interface TopicService { * separated by "<->" symbol. * * @param sendTo Email address of the receiver. - * @param email Email address of the sender. + * @param email Email address of the sender. * @return Unique private topic name. */ String generatePrivateName(String sendTo, String email); @@ -125,20 +124,20 @@ public interface TopicService { * Retrieves a list of favorite topics for the specified user. * * @param userDetails The details of the user for whom favorite topics are to be retrieved. - * @return A list of {@code TopicResponseDto} objects representing the user's favorite topics. + * @return A list of {@code TopicInfoResponseDto} objects representing the user's favorite topics. */ - List findAllFavouriteTopics(UserDetails userDetails); + List findAllFavouriteTopics(UserDetails userDetails); /** * Retrieves a list of popular public topics. *

- * This method returns a list of {@code TopicResponseDto} objects representing popular topics - * that are marked as public. The popularity is determined by the number of subscribers and messages + * This method returns a list of {@code TopicResponseDto} objects representing popular topics that + * are marked as public. The popularity is determined by the number of subscribers and messages * associated with each topic. * - * @return A list of {@code TopicResponseDto} objects representing popular public topics. + * @return A list of {@code TopicInfoResponseDto} objects representing popular public topics. * @see TopicResponseDto */ - List findPopularPublicTopics(); + List findPopularPublicTopics(); } diff --git a/src/test/java/com/chat/yourway/integration/controller/TopicControllerTest.java b/src/test/java/com/chat/yourway/integration/controller/TopicControllerTest.java index 299d1673..95b3a676 100644 --- a/src/test/java/com/chat/yourway/integration/controller/TopicControllerTest.java +++ b/src/test/java/com/chat/yourway/integration/controller/TopicControllerTest.java @@ -515,11 +515,7 @@ public void findAllPublic_shouldReturnListOfAllPublicTopics() throws Exception { .andExpect(jsonPath("$[0].createdBy").value(savedTopics.get(0).getCreatedBy())) .andExpect(jsonPath("$[1].topicName").value(savedTopics.get(1).getTopicName())) .andExpect(jsonPath("$[1].createdBy").value(savedTopics.get(1).getCreatedBy())) - .andExpect(jsonPath("$[*].createdAt").isNotEmpty()) - .andExpect(jsonPath("$[0].isPublic").value(true)) - .andExpect(jsonPath("$[1].isPublic").value(true)) - .andExpect(jsonPath("$[*].tags").isArray()) - .andExpect(jsonPath("$[*].topicSubscribers").isArray()); + .andExpect(jsonPath("$[*].createdAt").isNotEmpty()); } @Test diff --git a/src/test/java/com/chat/yourway/unit/service/impl/TopicServiceImplTest.java b/src/test/java/com/chat/yourway/unit/service/impl/TopicServiceImplTest.java index 5d113a5d..3fdbcf92 100644 --- a/src/test/java/com/chat/yourway/unit/service/impl/TopicServiceImplTest.java +++ b/src/test/java/com/chat/yourway/unit/service/impl/TopicServiceImplTest.java @@ -17,6 +17,7 @@ import com.chat.yourway.dto.request.TopicPrivateRequestDto; import com.chat.yourway.dto.request.TopicRequestDto; import com.chat.yourway.dto.response.TagResponseDto; +import com.chat.yourway.dto.response.TopicInfoResponseDto; import com.chat.yourway.dto.response.TopicResponseDto; import com.chat.yourway.exception.TopicAccessException; import com.chat.yourway.exception.TopicNotFoundException; @@ -351,7 +352,7 @@ public void findAllPublic_shouldReturnListOfTopicResponseDto() { when(topicRepository.findAllByIsPublicIsTrue()).thenReturn(topics); // When - List topicResponseDtos = topicService.findAllPublic(); + List topicResponseDtos = topicService.findAllPublic(); // Then assertThat(topicResponseDtos).isNotNull(); @@ -369,7 +370,7 @@ public void findAllPublic_shouldReturnEmptyListOfTopicResponseDto() { when(topicRepository.findAllByIsPublicIsTrue()).thenReturn(emptyList); // When - List topicResponseDtos = topicService.findAllPublic(); + List topicResponseDtos = topicService.findAllPublic(); // Then assertThat(topicResponseDtos).isNotNull(); @@ -486,4 +487,10 @@ private void assertTopicEquals(Topic topic, TopicResponseDto topicResponseDto) { assertThat(topicResponseDto.getTopicSubscribers()).isNull(); } + private void assertTopicEquals(Topic topic, TopicInfoResponseDto topicResponseDto) { + assertThat(topicResponseDto.getTopicName()).isEqualTo(topic.getTopicName()); + assertThat(topicResponseDto.getCreatedBy()).isEqualTo(topic.getCreatedBy()); + assertThat(topicResponseDto.getCreatedAt()).isEqualTo(topic.getCreatedAt()); + } + }