diff --git a/pom.xml b/pom.xml index dc2e28b62..261744b50 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ org.projectlombok lombok - 1.18.24 + 1.18.30 provided diff --git a/src/main/java/com/makersacademy/acebook/SecurityConfiguration.java b/src/main/java/com/makersacademy/acebook/SecurityConfiguration.java index a6829646e..c3df96c5a 100644 --- a/src/main/java/com/makersacademy/acebook/SecurityConfiguration.java +++ b/src/main/java/com/makersacademy/acebook/SecurityConfiguration.java @@ -28,11 +28,11 @@ protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/posts").hasRole("USER") .antMatchers("/users").permitAll() - .and().formLogin(); + .and().formLogin().loginPage("/login"); } @Bean public PasswordEncoder getPasswordEncoder() { return NoOpPasswordEncoder.getInstance(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/makersacademy/acebook/controller/FriendController.java b/src/main/java/com/makersacademy/acebook/controller/FriendController.java new file mode 100644 index 000000000..90608b45a --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/controller/FriendController.java @@ -0,0 +1,96 @@ +package com.makersacademy.acebook.controller; + +import com.makersacademy.acebook.model.Friend; +import com.makersacademy.acebook.model.User; +import com.makersacademy.acebook.repository.FriendRepository; +import com.makersacademy.acebook.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@Controller +public class FriendController { + + @Autowired + FriendRepository friendRepository; + @Autowired + UserRepository userRepository; + + @GetMapping("/users/friend") + public ModelAndView showFriends(Model model) { + ModelAndView modelAndView = new ModelAndView("/users/friend"); + modelAndView.addObject("friend", new Friend()); + return modelAndView; + } + + @GetMapping("/friends") + public ModelAndView showRequests(Principal principal) { + ModelAndView modelAndView = new ModelAndView("/users/friends"); + + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + + assert principalUser != null; + List pendingRequests = friendRepository.findByStatusAndReceiverId(principalUser.getId(), "PENDING"); + List acceptedRequests = friendRepository.findByStatusAndReceiverId(principalUser.getId(), "ACCEPTED"); + + ArrayList friendRequests = new ArrayList<>(); + for (Friend friend : pendingRequests) { + Optional optionalUser = userRepository.findById(friend.getRequesterId()); + User friendRequest = optionalUser.orElse(null); + friendRequests.add(friendRequest); + } + + ArrayList friends = new ArrayList<>(); + for (Friend friend : acceptedRequests) { + Optional optionalUser = userRepository.findById(friend.getRequesterId()); + User acceptedRequest = optionalUser.orElse(null); + friends.add(acceptedRequest); + } + + modelAndView.addObject("friendRequests", friendRequests); + modelAndView.addObject("friends", friends); + return modelAndView; + } + + @PostMapping("/handleFriendRequest") + public RedirectView handleFriendRequest(@RequestParam("requester_id") Long requesterId, + @RequestParam("receiver_id") Long receiverId, Principal principal) { + + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + assert principalUser != null; + + Friend friendRequest = new Friend(requesterId, receiverId, "PENDING"); + friendRepository.save(friendRequest); + + return new RedirectView("/users/" + receiverId); + } + + @PostMapping("/handleFriendConfirmation") + public String handleConfirmation(@RequestParam("action") String action, + @RequestParam("requesterId") Long requesterId, Principal principal) { + Optional currentUser = userRepository.findByUsername(principal.getName()); + User receiverId = currentUser.orElse(null); + assert receiverId != null; + + if ("accept".equals(action)) { + friendRepository.updateStatusByRequesterIdAndReceiverId(requesterId, receiverId.getId(), "ACCEPTED"); + } else if ("deny".equals(action)) { + friendRepository.updateStatusByRequesterIdAndReceiverId(requesterId, receiverId.getId(), "DENIED"); + } + + return "redirect:/friends"; + } +} diff --git a/src/main/java/com/makersacademy/acebook/controller/HomeController.java b/src/main/java/com/makersacademy/acebook/controller/HomeController.java index 2036ec7e0..40b353725 100644 --- a/src/main/java/com/makersacademy/acebook/controller/HomeController.java +++ b/src/main/java/com/makersacademy/acebook/controller/HomeController.java @@ -4,10 +4,24 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.view.RedirectView; +import javax.servlet.http.HttpSession; + @Controller public class HomeController { @RequestMapping(value = "/") public RedirectView index() { return new RedirectView("/posts"); } + + @RequestMapping("/login") + public String login() { + return "login"; + } + @RequestMapping("/logout") + public String logout(HttpSession session) { + session.removeAttribute("user_id"); + return "logout"; + } + } + diff --git a/src/main/java/com/makersacademy/acebook/controller/PostsController.java b/src/main/java/com/makersacademy/acebook/controller/PostsController.java index 57a7e5f4d..4afeedfbf 100644 --- a/src/main/java/com/makersacademy/acebook/controller/PostsController.java +++ b/src/main/java/com/makersacademy/acebook/controller/PostsController.java @@ -1,32 +1,104 @@ package com.makersacademy.acebook.controller; +import com.makersacademy.acebook.model.Comment; +import com.makersacademy.acebook.model.Friend; import com.makersacademy.acebook.model.Post; +import com.makersacademy.acebook.model.User; import com.makersacademy.acebook.repository.PostRepository; +import com.makersacademy.acebook.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; -import java.util.List; +import java.security.Principal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; @Controller public class PostsController { @Autowired - PostRepository repository; + PostRepository postRepository; + @Autowired + UserRepository userRepository; @GetMapping("/posts") - public String index(Model model) { - Iterable posts = repository.findAll(); - model.addAttribute("posts", posts); - model.addAttribute("post", new Post()); + public String index(Model model, Principal principal) { + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + assert principalUser != null; + + Iterable posts = postRepository.findAllByOrderByTimestampDesc(); + ArrayList> postsAndPosters = new ArrayList<>(); + + for (Post post : posts) { + HashMap entry = new HashMap<>(); + + Optional optionalUser = userRepository.findById(post.getUserId()); + User poster = optionalUser.orElse(null); + + entry.put(post, poster); + postsAndPosters.add(entry); + } + +// System.out.println(postsAndPosters); + + model.addAttribute("postsAndPosters", postsAndPosters); + model.addAttribute("newPost", new Post()); + model.addAttribute("profilePicture", principalUser.getImageUrl()); + return "posts/index"; } @PostMapping("/posts") - public RedirectView create(@ModelAttribute Post post) { - repository.save(post); + public RedirectView create(@ModelAttribute Post post, Principal principal) { + String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()); + post.setTimestamp(Timestamp.valueOf(timeStamp)); + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + post.setUserId(principalUser.getId()); + postRepository.save(post); return new RedirectView("/posts"); } + + @GetMapping("/post/{id}") + public String show(@PathVariable Long id, Model model) { + + Optional post = postRepository.findById(id); + Post currentPost = post.orElse(null); + model.addAttribute("currentPost", currentPost); + + Comment newComment = new Comment(); + model.addAttribute("newComment", newComment); + + return "posts/show"; + } + + @PostMapping("/post/{id}") + public ModelAndView createComment(@PathVariable Long id, @ModelAttribute Comment comment, Principal principal) { + +// to make a new comment for a post: +// comment content, post_id, user_id + comment.setPostId(id); + + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + comment.setUserId(principalUser.getId()); + + + + + + + + + ModelAndView modelAndView = new ModelAndView("/post/{id}"); + modelAndView.addObject("comment", new Comment()); + return modelAndView; + + } } diff --git a/src/main/java/com/makersacademy/acebook/controller/UsersController.java b/src/main/java/com/makersacademy/acebook/controller/UsersController.java index 3c46bf0a1..053708ec1 100644 --- a/src/main/java/com/makersacademy/acebook/controller/UsersController.java +++ b/src/main/java/com/makersacademy/acebook/controller/UsersController.java @@ -1,17 +1,28 @@ package com.makersacademy.acebook.controller; import com.makersacademy.acebook.model.Authority; +import com.makersacademy.acebook.model.Friend; import com.makersacademy.acebook.model.User; import com.makersacademy.acebook.repository.AuthoritiesRepository; +import com.makersacademy.acebook.repository.FriendRepository; import com.makersacademy.acebook.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.view.RedirectView; +import javax.servlet.http.HttpSession; +import java.security.Principal; +import java.util.List; +import java.util.Objects; +import java.util.ArrayList; +import java.util.Optional; + +import static java.lang.Boolean.valueOf; + @Controller public class UsersController { @@ -19,6 +30,8 @@ public class UsersController { UserRepository userRepository; @Autowired AuthoritiesRepository authoritiesRepository; + @Autowired + FriendRepository friendRepository; @GetMapping("/users/new") public String signup(Model model) { @@ -27,10 +40,45 @@ public String signup(Model model) { } @PostMapping("/users") - public RedirectView signup(@ModelAttribute User user) { + public RedirectView signup(@ModelAttribute User user, @RequestParam("fileData") String fileData) { + user.setImageUrl(fileData); + userRepository.save(user); Authority authority = new Authority(user.getUsername(), "ROLE_USER"); authoritiesRepository.save(authority); return new RedirectView("/login"); } + + @GetMapping("/users/{id}") + public ModelAndView show(@PathVariable Long id, Principal principal) { + Optional pageUser = userRepository.findById(id); + User user = pageUser.orElse(null); + + Optional currentUser = userRepository.findByUsername(principal.getName()); + User principalUser = currentUser.orElse(null); + assert principalUser != null; + + ModelAndView modelAndView = new ModelAndView("/users/show"); + modelAndView.addObject("currentUser", principalUser); + modelAndView.addObject("user", user); + + Iterable userRequests = friendRepository.findByRequesterIdOrReceiverId(principalUser.getId(), principalUser.getId()); + + for (Friend friend : userRequests) { + if (Objects.equals(friend.getReceiverId(), id) || Objects.equals(friend.getRequesterId(), id)) { + modelAndView.addObject("userRequest", friend); + break; + } else { + modelAndView.addObject("userRequest", null); + } + } + + if (principal.getName().equals(Objects.requireNonNull(user).getUsername())) { + modelAndView.addObject("isCurrentUser", true); + } else { + modelAndView.addObject("isCurrentUser", false); + } + + return modelAndView; + } } diff --git a/src/main/java/com/makersacademy/acebook/model/Authority.java b/src/main/java/com/makersacademy/acebook/model/Authority.java index 775b3bde2..2fb5d820e 100644 --- a/src/main/java/com/makersacademy/acebook/model/Authority.java +++ b/src/main/java/com/makersacademy/acebook/model/Authority.java @@ -1,7 +1,6 @@ package com.makersacademy.acebook.model; import lombok.Data; - import javax.persistence.*; @Data diff --git a/src/main/java/com/makersacademy/acebook/model/Comment.java b/src/main/java/com/makersacademy/acebook/model/Comment.java new file mode 100644 index 000000000..393fae403 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/Comment.java @@ -0,0 +1,57 @@ +package com.makersacademy.acebook.model; + + +import lombok.Data; + +import javax.persistence.*; + + +@Data +@Entity +@Table(name = "COMMENTS") +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String comment; + private Long postId; + private Long userId; + +// @ManyToOne(cascade = CascadeType.ALL) +// @JoinColumn(name = "id") +// private Post post; + + public Comment() {}; + + public Comment(String comment, Long postId, Long userId) { + this.comment = comment; + this.postId = postId; + this.userId = userId; + } + + public String getComment() { + return this.comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public Long getPostId() { + return this.postId; + } + + public void setPostId(Long postId) { + this.postId = postId; + } + + public Long getUserId() { + return this.userId; + } + + public void setUserId(Long UserId) { + this.userId = UserId; + } +} diff --git a/src/main/java/com/makersacademy/acebook/model/Friend.java b/src/main/java/com/makersacademy/acebook/model/Friend.java new file mode 100644 index 000000000..a78bb3c5e --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/Friend.java @@ -0,0 +1,40 @@ +package com.makersacademy.acebook.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.GenerationType; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +@Entity +@Table(name = "FRIENDS") +public class Friend { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Getter + private Long requesterId; + @Getter + private Long receiverId; + @Getter + private String status; + + public Friend() {} + + public Friend(Long requester_id, Long receiver_id, String status) { + this.requesterId = requester_id; + this.receiverId = receiver_id; + this.status = status; + } + + public void setRequesterId(Long requester_id) { this.requesterId = requester_id; } + public void setReceiverId(Long receiver_id) { this.receiverId = receiver_id; } + public void setStatus(String status) { this.status = status; } + +} diff --git a/src/main/java/com/makersacademy/acebook/model/Post.java b/src/main/java/com/makersacademy/acebook/model/Post.java index 0098de1b3..bc3320875 100644 --- a/src/main/java/com/makersacademy/acebook/model/Post.java +++ b/src/main/java/com/makersacademy/acebook/model/Post.java @@ -1,12 +1,11 @@ package com.makersacademy.acebook.model; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.GenerationType; +import javax.persistence.*; import lombok.Data; +import lombok.Getter; + +import java.sql.Timestamp; @Data @Entity @@ -16,14 +15,30 @@ public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Getter private String content; + @Getter + private Timestamp timestamp; + + @Getter + private Long userId; + +// @Getter +// @OneToMany(fetch = FetchType.LAZY, mappedBy = "postId", cascade = CascadeType.ALL) +// private List commentList; + public Post() {} - public Post(String content) { + public Post(String content, Timestamp timestamp, Long userId) { this.content = content; + this.timestamp = timestamp; + this.userId = userId; } - public String getContent() { return this.content; } public void setContent(String content) { this.content = content; } + public void setTimestamp(Timestamp timestamp) { this.timestamp = timestamp; } + + public void setUserId(Long userId) { this.userId = userId; } } diff --git a/src/main/java/com/makersacademy/acebook/model/User.java b/src/main/java/com/makersacademy/acebook/model/User.java index df2a0edf1..84a10a83d 100644 --- a/src/main/java/com/makersacademy/acebook/model/User.java +++ b/src/main/java/com/makersacademy/acebook/model/User.java @@ -7,6 +7,7 @@ import javax.persistence.GenerationType; import lombok.Data; +import lombok.Getter; import static java.lang.Boolean.TRUE; @@ -14,31 +15,37 @@ @Entity @Table(name = "USERS") public class User { + @Getter @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Getter private String username; + @Getter private String password; private boolean enabled; + @Getter String imageUrl; + public User() { this.enabled = TRUE; } - public User(String username, String password) { + public User(String username, String password, String imageUrl) { this.username = username; this.password = password; + this.imageUrl = imageUrl; this.enabled = TRUE; } - public User(String username, String password, boolean enabled) { + public User(String username, String password, String imageUrl, boolean enabled) { this.username = username; this.password = password; + this.imageUrl = imageUrl; this.enabled = enabled; } - public String getUsername() { return this.username; } - public String getPassword() { return this.password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } + public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } } diff --git a/src/main/java/com/makersacademy/acebook/repository/FriendRepository.java b/src/main/java/com/makersacademy/acebook/repository/FriendRepository.java new file mode 100644 index 000000000..7e646658d --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/repository/FriendRepository.java @@ -0,0 +1,26 @@ +package com.makersacademy.acebook.repository; + +import com.makersacademy.acebook.model.Friend; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +public interface FriendRepository extends CrudRepository { + @Query("SELECT f FROM Friend f WHERE f.receiverId = :receiverId AND f.status = :status") + List findByStatusAndReceiverId(@Param("receiverId") Long receiverId, @Param("status") String status); + + @Query("SELECT f FROM Friend f WHERE f.requesterId = :requesterId OR f.receiverId = :receiverId") + List findByRequesterIdOrReceiverId(@Param("requesterId") Long requesterId, @Param("receiverId") Long receiverId); + + @Transactional + @Modifying + @Query("UPDATE Friend f SET f.status = :status WHERE f.requesterId = :requesterId AND f.receiverId = :receiverId") + void updateStatusByRequesterIdAndReceiverId(@Param("requesterId") Long requesterId, + @Param("receiverId") Long receiverId, @Param("status") String status); + +} diff --git a/src/main/java/com/makersacademy/acebook/repository/PostRepository.java b/src/main/java/com/makersacademy/acebook/repository/PostRepository.java index d435e0ce1..de89f3772 100644 --- a/src/main/java/com/makersacademy/acebook/repository/PostRepository.java +++ b/src/main/java/com/makersacademy/acebook/repository/PostRepository.java @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; -public interface PostRepository extends CrudRepository { +import java.util.List; +public interface PostRepository extends CrudRepository { + public List findAllByOrderByTimestampDesc(); } diff --git a/src/main/java/com/makersacademy/acebook/repository/UserRepository.java b/src/main/java/com/makersacademy/acebook/repository/UserRepository.java index 2cccc950f..5f1f0eaa2 100644 --- a/src/main/java/com/makersacademy/acebook/repository/UserRepository.java +++ b/src/main/java/com/makersacademy/acebook/repository/UserRepository.java @@ -3,6 +3,8 @@ import com.makersacademy.acebook.model.User; import org.springframework.data.repository.CrudRepository; -public interface UserRepository extends CrudRepository { +import java.util.Optional; +public interface UserRepository extends CrudRepository { + Optional findByUsername(String username); } diff --git a/src/main/resources/db/migration/V10__update_userid_to_bigint.sql b/src/main/resources/db/migration/V10__update_userid_to_bigint.sql new file mode 100644 index 000000000..83c91ad42 --- /dev/null +++ b/src/main/resources/db/migration/V10__update_userid_to_bigint.sql @@ -0,0 +1,3 @@ +ALTER TABLE posts +ALTER COLUMN user_id +TYPE bigint; diff --git a/src/main/resources/db/migration/V11__update_userid_and_postid_to_bigint.sql b/src/main/resources/db/migration/V11__update_userid_and_postid_to_bigint.sql new file mode 100644 index 000000000..ccf94621b --- /dev/null +++ b/src/main/resources/db/migration/V11__update_userid_and_postid_to_bigint.sql @@ -0,0 +1,8 @@ +ALTER TABLE comments +ALTER COLUMN post_id TYPE bigint; + +ALTER TABLE comments +ALTER COLUMN user_id TYPE bigint; + + + diff --git a/src/main/resources/db/migration/V12__alter_friends_table.sql b/src/main/resources/db/migration/V12__alter_friends_table.sql new file mode 100644 index 000000000..b5e6e91b5 --- /dev/null +++ b/src/main/resources/db/migration/V12__alter_friends_table.sql @@ -0,0 +1,5 @@ +ALTER TABLE FRIENDS +ALTER COLUMN requester_id TYPE BIGINT; + +ALTER TABLE FRIENDS +ALTER COLUMN receiver_id TYPE BIGINT; diff --git a/src/main/resources/db/migration/V13__update_posts_with_timestamps.sql b/src/main/resources/db/migration/V13__update_posts_with_timestamps.sql new file mode 100644 index 000000000..93ea0610b --- /dev/null +++ b/src/main/resources/db/migration/V13__update_posts_with_timestamps.sql @@ -0,0 +1,44 @@ +update + posts +set + timestamp = '2023-11-14 11:13:24', + user_id = 3 +where + content = 'Second post'; + + +update + posts +set + timestamp = '2023-11-15 11:13:24', + user_id = 10 +where + content = 'fourth post'; + + +update + posts +set + timestamp = '2023-11-16 11:13:24', + user_id = 11 +where + content = 'third post'; + + +update + posts +set + timestamp = '2023-11-10 11:13:24', + user_id = 12 +where + content = 'sixth post'; + + +update + posts +set + timestamp = '2023-11-09 11:13:24', + user_id = 14 +where + content = 'fifth post'; + diff --git a/src/main/resources/db/migration/V15__add_user_1_for_testing.sql b/src/main/resources/db/migration/V15__add_user_1_for_testing.sql new file mode 100644 index 000000000..d4a312101 --- /dev/null +++ b/src/main/resources/db/migration/V15__add_user_1_for_testing.sql @@ -0,0 +1,3 @@ +INSERT INTO users +(username, password, enabled, image_url) +VALUES ('test_user', 'password22', true, null); \ No newline at end of file diff --git a/src/main/resources/db/migration/V16__insert_test_users_into_authorities_table.sql b/src/main/resources/db/migration/V16__insert_test_users_into_authorities_table.sql new file mode 100644 index 000000000..a2e5d1477 --- /dev/null +++ b/src/main/resources/db/migration/V16__insert_test_users_into_authorities_table.sql @@ -0,0 +1,6 @@ +insert into + authorities (username, authority) +values + ('zak', 'ROLE_USER'), + ('david', 'ROLE_USER'), + ('som', 'ROLE_USER'); \ No newline at end of file diff --git a/src/main/resources/db/migration/V17__add_test_user_to_authorities_table.sql b/src/main/resources/db/migration/V17__add_test_user_to_authorities_table.sql new file mode 100644 index 000000000..f82b1ff3f --- /dev/null +++ b/src/main/resources/db/migration/V17__add_test_user_to_authorities_table.sql @@ -0,0 +1,4 @@ +insert into + authorities (username, authority) +values + ('test_user', 'ROLE_USER'); \ No newline at end of file diff --git a/src/main/resources/db/migration/V18__update_imageUrl_column_to_text.sql b/src/main/resources/db/migration/V18__update_imageUrl_column_to_text.sql new file mode 100644 index 000000000..b0eb4c232 --- /dev/null +++ b/src/main/resources/db/migration/V18__update_imageUrl_column_to_text.sql @@ -0,0 +1,2 @@ +ALTER TABLE USERS +ALTER COLUMN image_url TYPE TEXT; \ No newline at end of file diff --git a/src/main/resources/db/migration/V3__update_existing_create_new_tables.sql b/src/main/resources/db/migration/V3__update_existing_create_new_tables.sql new file mode 100644 index 000000000..6032cbbfc --- /dev/null +++ b/src/main/resources/db/migration/V3__update_existing_create_new_tables.sql @@ -0,0 +1,38 @@ +ALTER TABLE posts +ADD COLUMN timestamp timestamp, +ADD COLUMN user_id integer, +ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id); + + +ALTER TABLE users +ADD image_url varchar(500); + + +CREATE TABLE likes ( + id bigserial PRIMARY KEY, + like_status boolean DEFAULT false, + user_id integer, + post_id integer, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (post_id) REFERENCES posts(id), + UNIQUE (user_id, post_id) +); + + +CREATE TABLE comments ( + id bigserial PRIMARY KEY, + comment varchar(281) NOT NULL, + post_id integer, + user_id integer, + FOREIGN KEY (post_id) REFERENCES posts(id), + FOREIGN KEY (user_id) REFERENCES users(id) +); + +CREATE TABLE friends ( + id bigserial PRIMARY KEY, + requester_id integer, + receiver_id integer, + FOREIGN KEY (requester_id) REFERENCES users(id), + FOREIGN KEY (receiver_id) REFERENCES users(id), + status varchar(50) check (status in ('ACCEPTED', 'PENDING', 'DENIED')) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V4__insert_test_data.sql b/src/main/resources/db/migration/V4__insert_test_data.sql new file mode 100644 index 000000000..26c1fd78d --- /dev/null +++ b/src/main/resources/db/migration/V4__insert_test_data.sql @@ -0,0 +1,7 @@ +insert into users (username, password, enabled, image_url) +values ('zak', '12345', TRUE, '/users/images/image.png'), + ('david', '123456', TRUE, '/users/images/another_image.png'); + + + + diff --git a/src/main/resources/db/migration/V5__insert_into_posts.sql b/src/main/resources/db/migration/V5__insert_into_posts.sql new file mode 100644 index 000000000..5df524f2a --- /dev/null +++ b/src/main/resources/db/migration/V5__insert_into_posts.sql @@ -0,0 +1,2 @@ +insert into posts (content, timestamp, user_id) +values ('first post', '2023-11-14 11:13:24', 2); \ No newline at end of file diff --git a/src/main/resources/db/migration/V9__add_new_user.sql b/src/main/resources/db/migration/V9__add_new_user.sql new file mode 100644 index 000000000..1b26a72e1 --- /dev/null +++ b/src/main/resources/db/migration/V9__add_new_user.sql @@ -0,0 +1,2 @@ +insert into users (username, password, enabled, image_url) +values ('som', '123457', TRUE, '/users/images/image_three.png'); diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 000000000..4a0cde1e5 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,32 @@ + + + Please log in + + +
+
+
+ Please Login +
+ Invalid username and password. +
+
+ You have been logged out. +
+ + + + +
+ +
+
+
+
+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/posts/index.html b/src/main/resources/templates/posts/index.html index 4eb260155..0ad34913f 100644 --- a/src/main/resources/templates/posts/index.html +++ b/src/main/resources/templates/posts/index.html @@ -1,26 +1,60 @@ - - + Acebook +
+ + Profile Image + + +
+

Posts

Signed in as
-
-

Content:

-

+ +

Content:

+

-
    -
  • +
      +
        +
      • +

        + +

        +

        +
        +

        + + +

        +
        +
      • +
    diff --git a/src/main/resources/templates/posts/show.html b/src/main/resources/templates/posts/show.html new file mode 100644 index 000000000..098f851cc --- /dev/null +++ b/src/main/resources/templates/posts/show.html @@ -0,0 +1,23 @@ + + + + + + + Acebook + + +

    +
    +

    Comment:

    +

    +
    +
      +
    • Comment 1
    • +
    • Comment 2
    • +
    • Comment 3
    • +
    + + \ No newline at end of file diff --git a/src/main/resources/templates/users/friends.html b/src/main/resources/templates/users/friends.html new file mode 100644 index 000000000..3ee7bc879 --- /dev/null +++ b/src/main/resources/templates/users/friends.html @@ -0,0 +1,27 @@ + + + + + Title + + + +
      +
    • + + +
      + + + +
      +
    • +
    + +
    +
      +
    • +
    + + + \ No newline at end of file diff --git a/src/main/resources/templates/users/new.html b/src/main/resources/templates/users/new.html index 2d763f396..13569c343 100644 --- a/src/main/resources/templates/users/new.html +++ b/src/main/resources/templates/users/new.html @@ -1,14 +1,31 @@ - - + Signup + +
    -

    Username:

    -

    Password:

    +

    Username:

    +

    Password:

    +

    Profile Picture:

    +

    diff --git a/src/main/resources/templates/users/show.html b/src/main/resources/templates/users/show.html new file mode 100644 index 000000000..6cef1d1f2 --- /dev/null +++ b/src/main/resources/templates/users/show.html @@ -0,0 +1,62 @@ + + + + + + + + + + + +
    +
    + + Profile Image + +
    + +
    + +

    + + + \ No newline at end of file diff --git a/src/test/java/SignUpTest.java b/src/test/java/SignUpTest.java index b0e16955b..fec9926fa 100644 --- a/src/test/java/SignUpTest.java +++ b/src/test/java/SignUpTest.java @@ -7,7 +7,10 @@ import org.junit.runner.RunWith; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -38,6 +41,41 @@ public void successfulSignUpRedirectsToSignIn() { driver.findElement(By.id("password")).sendKeys("password"); driver.findElement(By.id("submit")).click(); String title = driver.getTitle(); - Assert.assertEquals("Please sign in", title); + Assert.assertEquals("Please log in", title); + } + + + @Test + public void signUpButtonLinksToSignUpPage() { + driver.get("http://localhost:8080/login"); + driver.findElement(By.id("sign-up-btn")).click(); + String title = driver.getTitle(); + Assert.assertEquals("Signup", title); + } + + @Test + public void testBlankUsernameReturnsError() { + driver.get("http://localhost:8080/users/new"); + driver.findElement(By.id("username")).sendKeys(" "); + driver.findElement(By.id("password")).sendKeys("password"); + driver.findElement(By.id("submit")).click(); + // Explicitly wait for the error message to be visible + WebDriverWait wait = new WebDriverWait(driver, 10); + WebElement usernameError = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("usernameError"))); + String errorString = usernameError.getText(); + Assert.assertEquals("Please enter a username", errorString); + } + + @Test + public void testBlankPasswordReturnsError() { + driver.get("http://localhost:8080/users/new"); + driver.findElement(By.id("username")).sendKeys("username_1"); + driver.findElement(By.id("password")).sendKeys(" "); + driver.findElement(By.id("submit")).click(); + // Explicitly wait for the error message to be visible + WebDriverWait wait = new WebDriverWait(driver, 10); + WebElement passwordError = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("passwordError"))); + String errorString = passwordError.getText(); + Assert.assertEquals("Please enter a password", errorString); } } diff --git a/src/test/java/com/makersacademy/acebook/model/PostTest.java b/src/test/java/com/makersacademy/acebook/model/PostTest.java index 732aafc6e..be918f1d3 100644 --- a/src/test/java/com/makersacademy/acebook/model/PostTest.java +++ b/src/test/java/com/makersacademy/acebook/model/PostTest.java @@ -5,13 +5,22 @@ import org.junit.Test; +import java.sql.Timestamp; + public class PostTest { - private Post post = new Post("hello"); + Timestamp timestamp = Timestamp.valueOf("2007-09-23 10:10:10.0");; + + private Post post = new Post("hello", timestamp, 1L); @Test public void postHasContent() { assertThat(post.getContent(), containsString("hello")); } + @Test + public void postHasU() { + assertThat(post.getContent(), containsString("hello")); + } + } diff --git a/src/test/java/com/makersacademy/aceboook/controller/PostControllerTest.java b/src/test/java/com/makersacademy/aceboook/controller/PostControllerTest.java new file mode 100644 index 000000000..f71461271 --- /dev/null +++ b/src/test/java/com/makersacademy/aceboook/controller/PostControllerTest.java @@ -0,0 +1,125 @@ +package com.makersacademy.aceboook.controller; + +import com.github.javafaker.Faker; +import com.makersacademy.acebook.Application; +import com.makersacademy.acebook.model.Post; +import com.makersacademy.acebook.repository.PostRepository; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.List; + + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +public class PostControllerTest { + + + @Autowired + PostRepository postRepository; + + WebDriver driver; + Faker faker; + + @Before + public void setup() { + System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver"); + driver = new ChromeDriver(); + faker = new Faker(); + } + + @After + public void tearDown() { + driver.close(); + } + + @Test + public void testNewPostIsAtTheTopOfList() { + driver.get("http://localhost:8080/login"); + driver.findElement(By.id("username")).sendKeys("test_user"); + driver.findElement(By.id("password")).sendKeys("password22"); + driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("content")).sendKeys("New Post"); + driver.findElement(By.id("submit")).click(); + + WebElement ul = driver.findElement(By.tagName("ul")); + List postList = ul.findElements(By.tagName("li")); + + String newPost = postList.get(0).getText(); + + Assert.assertEquals("New Post", newPost); + } + + @Test + public void testNewPostHasUserIdAssigned() { + driver.get("http://localhost:8080/login"); + driver.findElement(By.id("username")).sendKeys("test_user"); + driver.findElement(By.id("password")).sendKeys("password22"); + driver.findElement(By.id("submit")).click(); + WebDriverWait wait = new WebDriverWait(driver, 10); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("content"))); + driver.findElement(By.id("content")).sendKeys("New Post"); + driver.findElement(By.id("submit")).click(); + List posts = postRepository.findAllByOrderByTimestampDesc(); + Post latestPost = posts.get(0); + Long userId = latestPost.getUserId(); + Long expected = 4L; + Assert.assertEquals(expected, userId); + } + + @Test + public void testCommentButtonNavigatesToPostPageFromPosts() { + driver.get("http://localhost:8080/login"); + driver.findElement(By.id("username")).sendKeys("test_user"); + driver.findElement(By.id("password")).sendKeys("password22"); + driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("content")).sendKeys("Testing Comment Button"); + driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("comment")).click(); + WebElement pElement = driver.findElement(By.tagName("p")); + String pText = pElement.getText(); + Assert.assertEquals("Testing Comment Button", pText); + + } + + @Test + public void testAddCommentReflectedInCommentsList() { + driver.get("http://localhost:8080/login"); + driver.findElement(By.id("username")).sendKeys("test_user"); + driver.findElement(By.id("password")).sendKeys("password22"); + driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("comment")).click(); + driver.findElement(By.id("addComment")).sendKeys("Here is my new comment!"); + driver.findElement(By.id("submitComment")).click(); + + WebElement commentsUL = driver.findElement(By.tagName("ul")); + List commentsLI = commentsUL.findElements(By.tagName("li")); + String comment = commentsLI.get(0).getText(); + + Assert.assertEquals("Here is my new comment!", comment); + + } + + + +// @Test +// public void testPostPageShowsPostAndListOfComments() { +// driver.get("http://localhost:8080/login"); +// driver.findElement(By.id("username")).sendKeys("test_user"); +// driver.findElement(By.id("password")).sendKeys("password22"); +// driver.findElement(By.id("submit")).click(); +// +// } +} diff --git a/src/test/java/com/makersacademy/aceboook/controller/UsersControllerTest.java b/src/test/java/com/makersacademy/aceboook/controller/UsersControllerTest.java new file mode 100644 index 000000000..0f8b6cd87 --- /dev/null +++ b/src/test/java/com/makersacademy/aceboook/controller/UsersControllerTest.java @@ -0,0 +1,55 @@ +package com.makersacademy.aceboook.controller; + +import com.github.javafaker.Faker; +import com.makersacademy.acebook.Application; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Application.class) +public class UsersControllerTest { + + WebDriver driver; + WebDriver page; + Faker faker; + + @Before + public void setup() { + System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver"); + driver = new ChromeDriver(); + faker = new Faker(); + } + + @After + public void tearDown() { + driver.close(); + } + + @Test + public void usersCanReadUsername() { + driver.get("localhost:8080/users/3"); + WebElement pElement = driver.findElement(By.tagName("p")); + String pText = pElement.getText(); + Assert.assertEquals("zak ", pText); + + } + +// @Test +// public void successfulSignUpRedirectsToSignIn() { +// driver.get("http://localhost:8080/users/new"); +// driver.findElement(By.id("username")).sendKeys(faker.name().firstName()); +// driver.findElement(By.id("password")).sendKeys("password"); +// driver.findElement(By.id("submit")).click(); +// String title = driver.getTitle(); +// Assert.assertEquals("Please sign in", title); +// } +}