Skip to content

Commit

Permalink
Merge pull request #134 from LionnoiL/156-messages-for-seller
Browse files Browse the repository at this point in the history
MP-156 ендпоіти для взаємодії із чатами і повідомленнями
  • Loading branch information
KovalBohdan-0 authored May 13, 2024
2 parents 1850b42 + 1ca1b43 commit 98bc4ac
Show file tree
Hide file tree
Showing 19 changed files with 579 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.extern.slf4j.Slf4j;
import org.petmarket.advertisements.advertisement.entity.Advertisement;
import org.petmarket.advertisements.advertisement.entity.AdvertisementStatus;
import org.petmarket.errorhandling.ItemNotFoundException;
import org.petmarket.users.entity.User;
import org.petmarket.users.service.UserService;
import org.springframework.security.access.AccessDeniedException;
Expand Down Expand Up @@ -36,7 +37,7 @@ public void checkViewAccess(Advertisement advertisement) {
if (!userService.isCurrentUserAdmin()) {
User user = userService.getCurrentUser();
if (user == null || !advertisement.getAuthor().equals(user)) {
throw new AccessDeniedException("Access denied to advertisement with id " + advertisement.getId());
throw new ItemNotFoundException("Advertisement was not found for user with id " + advertisement.getId());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ private ResponseEntity<Object> handleException(BadFrontendTokenException excepti
return buildExceptionBody(exception, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(UserBlackListedException.class)
private ResponseEntity<Object> handleException(UserBlackListedException exception) {
return buildExceptionBody(exception, HttpStatus.BAD_REQUEST);
}

@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException exception,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.petmarket.errorhandling;

public class UserBlackListedException extends RuntimeException {
public UserBlackListedException(String message) {
super(message);
}
}
63 changes: 63 additions & 0 deletions src/main/java/org/petmarket/message/controller/ChatController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.petmarket.message.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Positive;
import lombok.RequiredArgsConstructor;
import org.petmarket.message.dto.ChatResponseDto;
import org.petmarket.message.dto.UserChatsResponseDto;
import org.petmarket.message.service.MessageService;
import org.petmarket.utils.annotations.parametrs.ParameterId;
import org.petmarket.utils.annotations.parametrs.ParameterPageNumber;
import org.petmarket.utils.annotations.parametrs.ParameterPageSize;
import org.petmarket.utils.annotations.responses.ApiResponseForbidden;
import org.petmarket.utils.annotations.responses.ApiResponseSuccessful;
import org.petmarket.utils.annotations.responses.ApiResponseUnauthorized;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Chat")
@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping(value = "/v1/chat")
public class ChatController {
private final MessageService messageService;

@Operation(summary = "Get all chats of current user")
@GetMapping
@ApiResponseSuccessful
@ApiResponseUnauthorized
@PreAuthorize("isAuthenticated()")
public Page<UserChatsResponseDto> getLatestChats(
@ParameterPageNumber @RequestParam(defaultValue = "1") @Positive int page,
@ParameterPageSize @RequestParam(defaultValue = "30") @Positive int size) {
return messageService.getLatestChats(PageRequest.of(page - 1, size));
}

@Operation(summary = "Get chat messages by chat user id")
@GetMapping("/{chatUserId}")
@ApiResponseSuccessful
@ApiResponseUnauthorized
@ApiResponseForbidden
@PreAuthorize("isAuthenticated()")
public ChatResponseDto getChatMessages(
@ParameterPageNumber @RequestParam(defaultValue = "1") @Positive int page,
@ParameterPageSize @RequestParam(defaultValue = "30") @Positive int size,
@ParameterId @PathVariable Long chatUserId) {
return messageService.getChatWithUser(chatUserId,
PageRequest.of(page - 1, size));
}

@Operation(summary = "Mark chat messages as read")
@PutMapping("/{chatUserId}/read")
@ApiResponseSuccessful
@ApiResponseUnauthorized
@PreAuthorize("isAuthenticated()")
public void markChatMessagesAsRead(@ParameterId @PathVariable Long chatUserId) {
messageService.markChatMessagesAsRead(chatUserId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.petmarket.message.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.petmarket.message.dto.*;
import org.petmarket.message.service.MessageAccessCheckerService;
import org.petmarket.message.service.MessageService;
import org.petmarket.utils.annotations.parametrs.ParameterId;
import org.petmarket.utils.annotations.responses.*;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "Chat")
@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping(value = "/v1/messages")
public class MessageController {
private final MessageService messageService;
private final MessageAccessCheckerService messageAccessCheckerService;

@Operation(summary = "Create new message")
@PostMapping
@ApiResponseSuccessful
@ApiResponseBadRequest
@ApiResponseUnauthorized
@ApiResponseForbidden
@PreAuthorize("isAuthenticated()")
public void addMessage(@Valid @RequestBody MessageRequestDto messageRequestDto, BindingResult bindingResult) {
messageAccessCheckerService.checkCreateAccess(messageRequestDto);
messageService.addMessage(messageRequestDto);
}

@Operation(summary = "Delete message by id")
@DeleteMapping("/{id}")
@ApiResponseSuccessful
@ApiResponseUnauthorized
@ApiResponseForbidden
@ApiResponseNotFound
@PreAuthorize("isAuthenticated()")
public void deleteMessage(@ParameterId @PathVariable Long id) {
messageAccessCheckerService.checkDeleteAccess(id);
messageService.deleteMessage(id);
}

@Operation(summary = "Update message by id")
@PutMapping("/{id}")
@ApiResponseSuccessful
@ApiResponseBadRequest
@ApiResponseUnauthorized
@ApiResponseForbidden
@ApiResponseNotFound
@PreAuthorize("isAuthenticated()")
public void updateMessage(@ParameterId @PathVariable Long id,
@Valid @RequestBody MessageUpdateDto messageUpdateDto, BindingResult bindingResult) {
messageAccessCheckerService.checkUpdateAccess(List.of(messageService.getMessageById(id)));
messageService.updateMessage(id, messageUpdateDto);
}

@Operation(summary = "Mark message as read")
@PutMapping("/{id}/read")
@ApiResponseSuccessful
@ApiResponseUnauthorized
@ApiResponseForbidden
@PreAuthorize("isAuthenticated()")
public void markAsRead(@ParameterId @PathVariable Long id) {
messageAccessCheckerService.checkReadAccess(id);
messageService.markAsRead(id);
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/petmarket/message/dto/ChatResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.petmarket.message.dto;

import lombok.*;
import org.petmarket.users.dto.UserResponseDto;
import org.springframework.data.domain.Page;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChatResponseDto {
private UserResponseDto chatUser;
private Page<MessageResponseDto> messages;
}
25 changes: 25 additions & 0 deletions src/main/java/org/petmarket/message/dto/MessageRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.petmarket.message.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.*;
import org.springframework.validation.annotation.Validated;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Validated
public class MessageRequestDto {
@NotBlank(message = "The 'text' cannot be empty")
@Schema(example = "Hello, how are you?")
@Size(max = 10000, message = "The 'text' length must be less than or equal to 10000")
private String text;

@Schema(example = "1")
@JsonProperty("recipient_id")
private Long recipientId;
}
23 changes: 23 additions & 0 deletions src/main/java/org/petmarket/message/dto/MessageResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.petmarket.message.dto;

import lombok.*;
import org.petmarket.message.entity.MessageStatus;

import java.time.LocalDateTime;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MessageResponseDto {
private Long id;
private String text;
private MessageStatus status;
private LocalDateTime created;
private LocalDateTime updated;
private boolean sender;
private boolean edited;
private Long authorId;
private Long recipientId;
}
20 changes: 20 additions & 0 deletions src/main/java/org/petmarket/message/dto/MessageUpdateDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.petmarket.message.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.*;
import org.springframework.validation.annotation.Validated;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Validated
public class MessageUpdateDto {
@NotBlank(message = "The 'text' cannot be empty")
@Schema(example = "Hello, how are you?")
@Size(max = 10000, message = "The 'text' length must be less than or equal to 10000")
private String text;
}
15 changes: 15 additions & 0 deletions src/main/java/org/petmarket/message/dto/UserChatsResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.petmarket.message.dto;

import java.time.LocalDateTime;

public interface UserChatsResponseDto {
String getText();

LocalDateTime getCreated();

String getMessageStatus();

Long getChatUserId();

String getChatUserEmail();
}
64 changes: 64 additions & 0 deletions src/main/java/org/petmarket/message/entity/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.petmarket.message.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.petmarket.users.entity.User;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Table(name = "message")
@EqualsAndHashCode(of = {"id"})
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@Column(name = "text", nullable = false)
private String text;

@Column(name = "message_status", nullable = false)
@Enumerated(EnumType.STRING)
private MessageStatus status;

@CreatedDate
@Column(name = "created")
private LocalDateTime created;

@LastModifiedDate
@Column(name = "updated")
private LocalDateTime updated;

@ManyToOne
@JoinColumn(name = "author_id")
private User author;

@ManyToOne
@JoinColumn(name = "recipient_id")
private User recipient;
}
5 changes: 5 additions & 0 deletions src/main/java/org/petmarket/message/entity/MessageStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.petmarket.message.entity;

public enum MessageStatus {
UNREAD, READ
}
18 changes: 18 additions & 0 deletions src/main/java/org/petmarket/message/mapper/MessageMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.petmarket.message.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.petmarket.config.MapperConfig;
import org.petmarket.message.dto.MessageResponseDto;
import org.petmarket.message.entity.Message;
import org.petmarket.message.dto.MessageRequestDto;

@Mapper(config = MapperConfig.class)
public interface MessageMapper {
@Mapping(target = "recipient.id", source = "recipientId")
Message messageRequestDtoToMessage(MessageRequestDto messageRequestDto);

@Mapping(target = "authorId", source = "author.id")
@Mapping(target = "recipientId", source = "recipient.id")
MessageResponseDto messageToMessageResponseDto(Message message);
}
Loading

0 comments on commit 98bc4ac

Please sign in to comment.