Skip to content

Commit

Permalink
feature: Add CRUD posts API endpoints
Browse files Browse the repository at this point in the history
Task: 86972yq1k
  • Loading branch information
KinTrae committed Dec 21, 2024
1 parent 091f535 commit ca0eb05
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 116 deletions.
3 changes: 2 additions & 1 deletion backend/src/main/java/meowhub/backend/dtos/UserDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import meowhub.backend.constants.Roles;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Data
@Builder
Expand All @@ -17,7 +18,7 @@ public class UserDto {
private String surname;
private LocalDate birthdate;
private Genders gender;
private LocalDate createdAt;
private LocalDateTime createdAt;
private Roles role;
private boolean isAccountNonExpired;
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,83 @@
package meowhub.backend.posts.controllers;

import jakarta.websocket.server.PathParam;
import lombok.RequiredArgsConstructor;
import meowhub.backend.posts.dtos.PostDto;
import meowhub.backend.posts.services.PostService;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.webjars.NotFoundException;

@RestController
@RequestMapping("api/posts")
@RequiredArgsConstructor
public class PostController {
private final PostService postService;


@GetMapping()
public ResponseEntity<Page<PostDto>> getAllUsersPosts(@AuthenticationPrincipal UserDetails userDetails, @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "10") int pageSize) {
Page<PostDto> posts = postService.getAllPosts(userDetails.getUsername(), pageNo, pageSize);
public ResponseEntity<Page<PostDto>> getPosts(@AuthenticationPrincipal UserDetails userDetails, @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "10") int pageSize) {
Page<PostDto> posts = postService.getPosts(userDetails.getUsername(), pageNo, pageSize);
return ResponseEntity.ok(posts);
}

@GetMapping("/{login}")
public ResponseEntity<Page<PostDto>> getPostsForUser(@PathParam("login") String login, @AuthenticationPrincipal UserDetails userDetails, @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "10") int pageSize) {
Page<PostDto> posts = postService.getAllPostsForUser(login, userDetails.getUsername(), pageNo, pageSize);
return ResponseEntity.ok(posts);
public ResponseEntity<Page<PostDto>> getPostsForUser(@PathVariable("login") String login, @AuthenticationPrincipal UserDetails userDetails, @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "10") int pageSize) {
try {
Page<PostDto> posts = postService.getPostsForUser(login, userDetails.getUsername(), pageNo, pageSize);
return ResponseEntity.ok(posts);
} catch (UsernameNotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested user not found").build();
}
}

@PostMapping("")
public ResponseEntity<PostDto> createPost(@RequestParam String content, @AuthenticationPrincipal UserDetails userDetails) {
PostDto postDto = postService.createPost(userDetails.getUsername(), content);
return ResponseEntity.ok(postDto);
try {
PostDto postDto = postService.createPost(userDetails.getUsername(), content);
return ResponseEntity.ok(postDto);
} catch (UsernameNotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested user not found").build();
}
}

@DeleteMapping("/{postId}")
public ResponseEntity<Void> deletePost(@PathParam("postId") String postId, @AuthenticationPrincipal UserDetails userDetails) {
postService.deletePost(userDetails.getUsername(), postId);
return ResponseEntity.ok().build();
public ResponseEntity<Void> deletePost(@PathVariable("postId") String postId, @AuthenticationPrincipal UserDetails userDetails) {
try {
postService.deletePost(userDetails.getUsername(), postId);
return ResponseEntity.ok().build();
} catch (UsernameNotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested user not found").build();
} catch (NotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested post not found").build();
}
}

@PutMapping("/{postId}")
public ResponseEntity<PostDto> updatePost(@PathParam("postId") String postId, @RequestParam String content, @AuthenticationPrincipal UserDetails userDetails) {
PostDto postDto = postService.updatePost(userDetails.getUsername(), postId, content);
return ResponseEntity.ok(postDto);
public ResponseEntity<PostDto> updatePost(@PathVariable("postId") String postId, @RequestParam String content, @AuthenticationPrincipal UserDetails userDetails) {
try {
PostDto postDto = postService.updatePost(userDetails.getUsername(), postId, content);
return ResponseEntity.ok(postDto);
} catch (UsernameNotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested user not found").build();
} catch (NotFoundException exception) {
exception.printStackTrace();
return ResponseEntity.notFound().eTag("Requested post not found").build();
}
}
}
}
10 changes: 6 additions & 4 deletions backend/src/main/java/meowhub/backend/posts/dtos/PostDto.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package meowhub.backend.posts.dtos;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import meowhub.backend.users.dtos.BasicUserInfoDto;

import java.time.LocalDate;
import java.util.List;
import java.time.LocalDateTime;

@Builder
@Data
@AllArgsConstructor
public class PostDto {
private String id;
private String content;
private BasicUserInfoDto author;
private List<CommentDto> comments;
private LocalDate createdAt;
private Long numberOfComments;
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -1,61 +1,118 @@
package meowhub.backend.posts.repositories;

import meowhub.backend.posts.dtos.PostDto;
import meowhub.backend.posts.models.Post;
import meowhub.backend.users.models.User;
import org.springframework.data.domain.Page;
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 org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface PostRepository extends JpaRepository<Post, String> {
Page<Post> findAllByUserLogin(String login, Pageable pageable);
Optional<Post> findByUserLoginAndId(String login, String id);

@Query(value = """
SELECT new meowhub.backend.posts.dtos.PostDto (
p.id,
p.contentHtml,
new meowhub.backend.users.dtos.BasicUserInfoDto (
u.id,
u.name,
u.surname,
u.login,
pictures.picture
),
(SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id),
p.createdAt
)
FROM User u
JOIN u.posts p
LEFT JOIN Picture pictures ON pictures.user.id = u.id
LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id
LEFT JOIN u.postsPrivacy postsPrivacy
WHERE postsPrivacy.code = 'PUBLIC'
OR (postsPrivacy.code = 'FRIENDS_ONLY' AND u.id IN (u.id,
(SELECT r.receiver.id
FROM User sender
JOIN sender.userRelationsSender r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND sender.login = :requestedBy
UNION
SELECT r.sender.id
FROM User receiver
JOIN receiver.userRelationsReceiver r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND receiver.login = :requestedBy
)))
""")
Page<PostDto> findIfPublicOrFriends(@Param("requestedBy") String requestedBy, Pageable pageable);

@Query("""
SELECT p
FROM User u
JOIN u.posts p
LEFT JOIN u.postsPrivacy postsPrivacy
WHERE u.login = :login
AND (postsPrivacy.code = 'PUBLIC'
OR (postsPrivacy.code = 'FRIENDS' AND u.id IN (
SELECT r.receiver.id
FROM User sender
JOIN sender.userRelationsSender r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND sender.login = :requestedBy
UNION
SELECT r.sender.id
FROM User receiver
JOIN receiver.userRelationsReceiver r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND receiver.login = :requestedBy
)))
""")
Page<Post> findAllByUserLoginIfPublicOrFriend(@Param("login") String login, @Param("requestedBy") String requestedBy, Pageable pageable);
SELECT new meowhub.backend.posts.dtos.PostDto (
p.id,
p.contentHtml,
new meowhub.backend.users.dtos.BasicUserInfoDto (
u.id,
u.name,
u.surname,
u.login,
pictures.picture
),
(SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id),
p.createdAt
)
FROM User u
JOIN u.posts p
LEFT JOIN Picture pictures ON pictures.user.id = u.id
LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id
LEFT JOIN u.postsPrivacy postsPrivacy
WHERE u.login = :login
AND (postsPrivacy.code = 'PUBLIC'
OR (postsPrivacy.code = 'FRIENDS_ONLY' AND u.id IN (
SELECT r.receiver.id
FROM User sender
JOIN sender.userRelationsSender r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND sender.login = :requestedBy
UNION
SELECT r.sender.id
FROM User receiver
JOIN receiver.userRelationsReceiver r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND receiver.login = :requestedBy)
)) ORDER BY p.createdAt DESC
""")
Page<PostDto> findByUserLoginIfPublicOrFriend(@Param("login") String login, @Param("requestedBy") String requestedBy, Pageable pageable);

@Query("""
SELECT r.receiver.id
FROM User sender
JOIN sender.userRelationsSender r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND sender.login = :login
UNION
SELECT r.sender.id
FROM User receiver
JOIN receiver.userRelationsReceiver r
JOIN r.relationType relType
WHERE relType.code = 'FRIENDS'
AND receiver.login = :login
SELECT new meowhub.backend.posts.dtos.PostDto (
p.id,
p.contentHtml,
new meowhub.backend.users.dtos.BasicUserInfoDto (
u.id,
u.name,
u.surname,
u.login,
pictures.picture
),
(SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id),
p.createdAt
)
FROM User u
JOIN u.posts p
LEFT JOIN Picture pictures ON pictures.user.id = u.id
LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id
LEFT JOIN u.postsPrivacy postsPrivacy
WHERE u.login = :login
ORDER BY p.createdAt DESC
""")
List<User> findFriendsByLogin(@Param("login") String login);
Page<PostDto> findOwn(@Param("login") String login, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import org.springframework.data.domain.Page;

public interface PostService {
Page<PostDto> getAllPosts(String requestedBy, int pageNo, int pageSize);
Page<PostDto> getPosts(String requestedBy, int pageNo, int pageSize);

Page<PostDto> getAllPostsForUser(String login, String requestedBy, int pageNo, int pageSize);
Page<PostDto> getPostsForUser(String login, String requestedBy, int pageNo, int pageSize);

PostDto createPost(String login, String content);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ public class PostServiceImpl implements PostService {
private final UserRepository userRepository;

@Override
public Page<PostDto> getAllPosts(String requestedBy, int pageNo, int pageSize) {
public Page<PostDto> getPosts(String requestedBy, int pageNo, int pageSize) {
Pageable pageable = PageRequest.of(pageNo, pageSize);
return postRepository.findAll(pageable).map(this::convertToPostDto);
return postRepository.findIfPublicOrFriends(requestedBy, pageable);
}

@Override
public Page<PostDto> getAllPostsForUser(String login, String requestedBy, int pageNo, int pageSize) {
public Page<PostDto> getPostsForUser(String login, String requestedBy, int pageNo, int pageSize) {
Pageable pageable = PageRequest.of(pageNo, pageSize);
userRepository.findByLogin(login)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User: '%s' not found", login)));

if (login.equals(requestedBy)) {
return postRepository.findAllByUserLogin(login, pageable).map(this::convertToPostDto);
return postRepository.findOwn(login, pageable);
} else {
return postRepository.findByUserLoginIfPublicOrFriend(login, requestedBy, pageable);
}

return null;
}

@Override
Expand All @@ -61,9 +64,7 @@ public void deletePost(String login, String postId) {
}

private Post findUserPost(String login, String postId) {
userRepository.findByLogin(login)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User: '%s' not found", login)));

userRepository.findByLogin(login).orElseThrow(() -> new UsernameNotFoundException(String.format("User: '%s' not found", login)));
return postRepository.findByUserLoginAndId(login, postId)
.orElseThrow(() -> new NotFoundException(String.format("Post with id: '%s' not found for user '%s'", postId, login)));
}
Expand All @@ -75,6 +76,7 @@ private PostDto convertToPostDto(Post post) {

BasicUserInfoDto author = userRepository.findBasicUserInfoByLogin(post.getUser().getLogin()).orElseThrow();
return PostDto.builder()
.id(post.getId())
.content(post.getContentHtml())
.author(author)
.createdAt(post.getCreatedAt())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Configuration
@EnableWebSecurity
Expand Down Expand Up @@ -83,7 +84,7 @@ public CommandLineRunner initData(RoleRepository roleRepository, UserRepository
user1.setAccountNonLocked(false);
user1.setBirthdate(LocalDate.of(1990, 1, 1));
user1.setCredentialsNonExpired(true);
user1.setCredentialsExpiryDate(LocalDate.now().plusYears(1));
user1.setCredentialsExpiryDate(LocalDateTime.now().plusYears(1));
user1.setRole(userRole);
user1.setPostsPrivacy(publicSetting);
user1.setFriendsPrivacy(publicSetting);
Expand All @@ -103,7 +104,7 @@ public CommandLineRunner initData(RoleRepository roleRepository, UserRepository
admin.setAccountNonLocked(false);
admin.setBirthdate(LocalDate.of(1980, 1, 1));
admin.setCredentialsNonExpired(true);
admin.setCredentialsExpiryDate(LocalDate.now().plusYears(1));
admin.setCredentialsExpiryDate(LocalDateTime.now().plusYears(1));
admin.setRole(adminRole);
admin.setPostsPrivacy(publicSetting);
admin.setFriendsPrivacy(publicSetting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class UserController {
private final UserService userService;

@GetMapping("get-basic-user-info")
@GetMapping("basic-user-info")
public ResponseEntity<BasicUserInfoDto> getBasicUserInfo(String login) {
try {
userService.getBasicUserInfo(login);
Expand Down
Loading

0 comments on commit ca0eb05

Please sign in to comment.