diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..bdb1e7d06 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index c64b53754..1b7d0efb3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ dependency-reduced-pom.xml .factorypath .project .settings/ +application.yaml +application.yml \ No newline at end of file diff --git a/pom.xml b/pom.xml index 32aeb8fc4..93463f585 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,13 @@ 21 + + + org.junit.vintage + junit-vintage-engine + 5.11.1 + org.springframework.boot spring-boot-starter-data-jpa @@ -42,6 +48,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-validation + org.flywaydb flyway-core @@ -66,6 +76,11 @@ lombok provided + + org.junit.vintage + junit-vintage-engine + 5.11.1 + junit junit @@ -95,6 +110,11 @@ org.thymeleaf.extras thymeleaf-extras-springsecurity6 + + org.junit.vintage + junit-vintage-engine + 5.11.1 + diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 000000000..c2fe07baf Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 000000000..8594c23dc Binary files /dev/null and b/src/main/.DS_Store differ diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store new file mode 100644 index 000000000..9279a7e35 Binary files /dev/null and b/src/main/java/.DS_Store differ diff --git a/src/main/java/com/.DS_Store b/src/main/java/com/.DS_Store new file mode 100644 index 000000000..56edf86c1 Binary files /dev/null and b/src/main/java/com/.DS_Store differ diff --git a/src/main/java/com/makersacademy/.DS_Store b/src/main/java/com/makersacademy/.DS_Store new file mode 100644 index 000000000..d146cc031 Binary files /dev/null and b/src/main/java/com/makersacademy/.DS_Store differ diff --git a/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java b/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java index 542bce54c..fa9d2205b 100644 --- a/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java +++ b/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java @@ -1,21 +1,28 @@ package com.makersacademy.acebook.config; +import com.makersacademy.acebook.model.User; +import com.makersacademy.acebook.repository.UserRepository; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.io.IOException; +import java.util.Optional; import static org.springframework.security.config.Customizer.withDefaults; @@ -23,6 +30,9 @@ @EnableWebSecurity public class SecurityConfiguration { + @Autowired + private UserRepository userRepository; + @Value("${okta.oauth2.issuer}") private String issuer; @Value("${okta.oauth2.client-id}") @@ -31,7 +41,7 @@ public class SecurityConfiguration { @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { http - .csrf(csrf -> csrf.disable()) + .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/", "/images/**").permitAll() .anyRequest().authenticated() @@ -40,7 +50,15 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - response.sendRedirect("/users/after-login"); + DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal(); + String username = (String) principal.getAttributes().get("email"); + Optional user = userRepository.findUserByUsername(username); + if (user.isEmpty()) { + response.sendRedirect("/users/newUser"); // Redirect if user is missing + } else { + response.sendRedirect("/users/after-login"); + //^^ if user is not in OUR database, they get redirected + } } }) ) diff --git a/src/main/java/com/makersacademy/acebook/controller/HomeController.java b/src/main/java/com/makersacademy/acebook/controller/HomeController.java index 2036ec7e0..79337e122 100644 --- a/src/main/java/com/makersacademy/acebook/controller/HomeController.java +++ b/src/main/java/com/makersacademy/acebook/controller/HomeController.java @@ -1,13 +1,37 @@ package com.makersacademy.acebook.controller; +import com.makersacademy.acebook.model.User; +import com.makersacademy.acebook.repository.UserRepository; +import org.springframework.security.core.Authentication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.view.RedirectView; +import java.util.Optional; + @Controller public class HomeController { - @RequestMapping(value = "/") - public RedirectView index() { - return new RedirectView("/posts"); + + @Autowired + UserRepository userRepository; + + @ModelAttribute("user") + Optional findUser(Authentication authentication) { + DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal(); + String username = (String) principal.getAttributes().get("email"); + return userRepository.findUserByUsername(username); + } + + // Routes ------ + + @GetMapping(value = "/") + public String index(@ModelAttribute("user") Optional user) { + return user. + map(_user -> "redirect:/users/" + _user.getUsername()) + .orElse("redirect:/users/newUser"); } } diff --git a/src/main/java/com/makersacademy/acebook/controller/NavigationController.java b/src/main/java/com/makersacademy/acebook/controller/NavigationController.java new file mode 100644 index 000000000..661f4b60d --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/controller/NavigationController.java @@ -0,0 +1,70 @@ +// STOP PEOPLE BEING SNEAKY IF NOT REGISTERED +/* + import com.makersacademy.acebook.repository.UserRepository; + import org.springframework.security.core.Authentication; <- replaces the tomcat one + @Autowired + UserRepository userRepository; + + public String exampleMethod(Authentication authentication) + DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal(); + String username = (String) principal.getAttributes().get("email"); + Optional user = userRepository.findUserByUsername(username); + if (user.isEmpty()) { + return "redirect:/users/newUser"; + } + return "whateverhtmlyouwant" +*/ + +package com.makersacademy.acebook.controller; + +import com.makersacademy.acebook.model.User; +import com.makersacademy.acebook.repository.UserRepository; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.Optional; + + +@Controller +public class NavigationController { + + @Autowired + UserRepository userRepository; + + @ModelAttribute("user") + public Optional getUser(Authentication authentication) { + DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal(); + String username = (String) principal.getAttributes().get("email"); + return userRepository.findUserByUsername(username); + } + + + // Routes ------ + + @GetMapping("/profile") + public String profile(@ModelAttribute("user") Optional user) { + return user.isEmpty() ? "redirect:/users/newUser" : "profile"; + } + + @GetMapping("/settings") + public String settings(@ModelAttribute("user") Optional user) { + return user.isEmpty() ? "redirect:users/newUser" : "settings"; + } + + @PostMapping("/settings") + public String settings(@Valid User user, BindingResult bindingResult) { + if (bindingResult.hasErrors()) { + return "settings"; + } else { + userRepository.save(user); + return "redirect:/posts"; + } + } +} diff --git a/src/main/java/com/makersacademy/acebook/controller/PostsController.java b/src/main/java/com/makersacademy/acebook/controller/PostsController.java index 57a7e5f4d..b22a17a74 100644 --- a/src/main/java/com/makersacademy/acebook/controller/PostsController.java +++ b/src/main/java/com/makersacademy/acebook/controller/PostsController.java @@ -1,14 +1,22 @@ package com.makersacademy.acebook.controller; import com.makersacademy.acebook.model.Post; +import com.makersacademy.acebook.model.User; + +import com.makersacademy.acebook.repository.UserRepository; import com.makersacademy.acebook.repository.PostRepository; +import org.springframework.security.core.Authentication; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; + import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.view.RedirectView; +import java.security.Principal; import java.util.List; +import java.util.Optional; @Controller public class PostsController { @@ -16,12 +24,41 @@ public class PostsController { @Autowired PostRepository repository; + @Autowired + UserRepository userRepository; + + + @GetMapping("/posts") - public String index(Model model) { + public String index(Model model, Authentication authentication) { + DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal(); + String username = (String) principal.getAttributes().get("email"); + // code above to get email from the authenticator + Optional user = userRepository.findUserByUsername(username); + // ^^ optional user, theoretical + + if (user.isEmpty()) { + return "redirect:/users/newUser"; // Redirect if not registered + } + // ^^ if the user is not saved in our database, they get redirected to the registration page + + Long userId = user.get().getId(); // getting id from database - checking that id is connected + String email = user.get().getUsername(); // getting email in same way +// public String index(Model model, @AuthenticationPrincipal OAuth2User principal) { + // String userName = principal.getAttribute("email"); +// Optional user = userRepository.findUserByUsername(userName); +// long id = user.map(User::getId).orElse(999999999999L); +// model.addAttribute("userID",id); + Iterable posts = repository.findAll(); model.addAttribute("posts", posts); model.addAttribute("post", new Post()); - return "posts/index"; + + // code below to get userId and email from database + model.addAttribute("userId", userId); + model.addAttribute("email", email); + + return "index"; } @PostMapping("/posts") diff --git a/src/main/java/com/makersacademy/acebook/controller/UsersController.java b/src/main/java/com/makersacademy/acebook/controller/UsersController.java index a7c9db1d8..2194a83ea 100644 --- a/src/main/java/com/makersacademy/acebook/controller/UsersController.java +++ b/src/main/java/com/makersacademy/acebook/controller/UsersController.java @@ -2,13 +2,20 @@ import com.makersacademy.acebook.model.User; import com.makersacademy.acebook.repository.UserRepository; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; -@RestController +import java.util.Optional; + +@Controller +@RequestMapping public class UsersController { @Autowired UserRepository userRepository; @@ -21,10 +28,33 @@ public RedirectView afterLogin() { .getPrincipal(); String username = (String) principal.getAttributes().get("email"); - userRepository - .findUserByUsername(username) - .orElseGet(() -> userRepository.save(new User(username))); + Optional user = userRepository.findUserByUsername(username); + if (user.isPresent()){ + return new RedirectView("/posts"); // redirect to posts if registered + } else { + return new RedirectView("/users/newUser"); // redirect to registration if missing + } + + } + @GetMapping("/users/newUser") + public String afterSignUp(@ModelAttribute("our_user") User user) { + DefaultOidcUser principal = (DefaultOidcUser) SecurityContextHolder + .getContext() + .getAuthentication() + .getPrincipal(); + + String username = (String) principal.getAttributes().get("email"); + user.setUsername(username); // automatically fills in username on new user field, perhaps make this not allowed to change? once we add FN and LN + return "newUser"; - return new RedirectView("/posts"); + } + @PostMapping("/users/newUser") + public String saveNewUser(@Valid @ModelAttribute("our_user") User user, BindingResult result) { + if (result.hasErrors()) { // error messages come from class constraints (needs dependency) + return "newUser"; //stays on page + } else { + userRepository.save(user); // this saves user to database, eventually fill out parameters as database changes to include first name, last name, dob etc + return "redirect:/posts"; // redirects to posts + } } } 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..4dd4d2b93 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/Comment.java @@ -0,0 +1,35 @@ +package com.makersacademy.acebook.model; + + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.Instant; + +@Data +@Entity +@Table(name = "comments") +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String content; + + @Column(name = "user_id") + private int userId; + + @Column(name = "post_id") + private int postId; + + @Column(name = "comment_timestamp") + private Timestamp commentTimestamp; + + public Comment(String content, int userId, int postId){ + this.content = content; + this.userId = userId; + this.postId = postId; + this.commentTimestamp = Timestamp.from(Instant.now()); + } +} diff --git a/src/main/java/com/makersacademy/acebook/model/CommentLike.java b/src/main/java/com/makersacademy/acebook/model/CommentLike.java new file mode 100644 index 000000000..c87db57f8 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/CommentLike.java @@ -0,0 +1,32 @@ +package com.makersacademy.acebook.model; + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.Instant; + +@Data +@Entity +@Table(name = "comment_likes") +public class CommentLike { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id") + private int userId; + + @Column(name = "comment_id") + private int commentId; + + @Column(name = "like_timestamp") + private Timestamp timestamp; + + public CommentLike(int userId, int commentId){ + this.userId = userId; + this.commentId = commentId; + this.timestamp = Timestamp.from(Instant.now()); + } +} diff --git a/src/main/java/com/makersacademy/acebook/model/Friendship.java b/src/main/java/com/makersacademy/acebook/model/Friendship.java new file mode 100644 index 000000000..cc0b55b81 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/Friendship.java @@ -0,0 +1,31 @@ +package com.makersacademy.acebook.model; + + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.Instant; + +@Data +@Entity +@Table(name = "friendships") +public class Friendship { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "lower_user_id") + private int lowerUserId; + + @Column(name = "higher_user_id") + private int higherUserId; + private Timestamp friendshipTimestamp; + + public Friendship(int lowerUserId, int higherUserId){ + this.lowerUserId = lowerUserId; + this.higherUserId = higherUserId; + this.friendshipTimestamp = Timestamp.from(Instant.now()); + } +} diff --git a/src/main/java/com/makersacademy/acebook/model/Post.java b/src/main/java/com/makersacademy/acebook/model/Post.java index 33492c6b1..565b44ed0 100644 --- a/src/main/java/com/makersacademy/acebook/model/Post.java +++ b/src/main/java/com/makersacademy/acebook/model/Post.java @@ -4,19 +4,46 @@ import lombok.Data; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; + @Data @Entity -@Table(name = "POSTS") +@Table(name = "posts") public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String content; + private String photo; + + @Column(name = "user_id") + private int userId; + + @Column(name = "post_timestamp") + private Timestamp timestamp; public Post() {} + // used in the legacy code public Post(String content) { this.content = content; } + + // used when posting without a picture + public Post(String content,int userId) { + this.content = content; + this.userId = userId; + this.timestamp = Timestamp.from(Instant.now()); + } + + // posting with a picture + public Post(String content,int userId, String photo) { + this.content = content; + this.userId = userId; + this.timestamp = Timestamp.from(Instant.now()); + this.photo = photo; + } } diff --git a/src/main/java/com/makersacademy/acebook/model/PostLike.java b/src/main/java/com/makersacademy/acebook/model/PostLike.java new file mode 100644 index 000000000..b36318d10 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/PostLike.java @@ -0,0 +1,36 @@ +package com.makersacademy.acebook.model; + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.Instant; + +@Data +@Entity +@Table(name = "post_likes") +public class PostLike { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id") + private int userId; + + @Column(name = "post_id") + private int postId; + + @Column(name = "like_timestamp") + private Timestamp timestamp; + + public PostLike(int user_id, int post_id){ + this.userId = user_id; + this.postId = post_id; + this.timestamp = Timestamp.from(Instant.now()); + } + + public PostLike() { + + } +} diff --git a/src/main/java/com/makersacademy/acebook/model/User.java b/src/main/java/com/makersacademy/acebook/model/User.java index 6013fbe23..729267bc4 100644 --- a/src/main/java/com/makersacademy/acebook/model/User.java +++ b/src/main/java/com/makersacademy/acebook/model/User.java @@ -1,31 +1,70 @@ package com.makersacademy.acebook.model; +import com.makersacademy.acebook.model.validation.DateOfBirthConstraint; import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDate; +import java.util.Date; import static java.lang.Boolean.TRUE; @Data @Entity -@Table(name = "USERS") +@Getter @Setter + +@Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + + @NotBlank(message = "Username cannot be blank.") // error message: means we don't need to specify elsewhere that it can't be blank private String username; private boolean enabled; + private String avatar; + + @NotNull(message = "Please set your date of birth") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @DateOfBirthConstraint + private LocalDate dob; + + @NotBlank(message = "Please enter your first name") + @Column(name = "first_name") + private String firstName; + + @NotBlank(message = "Please enter your last name") + @Column(name = "last_name") + private String lastName; public User() { this.enabled = TRUE; } + //used in legacy code public User(String username) { this.username = username; this.enabled = TRUE; } - public User(String username, boolean enabled) { + // used - assumes there will be an avatar + public User(String username,String firstName, String lastName, LocalDate dob, String avatar) { this.username = username; - this.enabled = enabled; + this.avatar = avatar; + this.firstName = firstName; + this.lastName = lastName; + this.dob = dob; + this.avatar = avatar; + this.enabled = TRUE; } + + } diff --git a/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java new file mode 100644 index 000000000..a5f6a3864 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java @@ -0,0 +1,16 @@ +package com.makersacademy.acebook.model.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = DateOfBirthValidator.class) +@Target( {ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DateOfBirthConstraint { + String message() default "Users must be 14 or older"; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java new file mode 100644 index 000000000..7cd16d3a2 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java @@ -0,0 +1,22 @@ +package com.makersacademy.acebook.model.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.time.LocalDate; + + + +public class DateOfBirthValidator implements ConstraintValidator { + + @Override + public void initialize(DateOfBirthConstraint dob) { } + + @Override + public boolean isValid(LocalDate dob, ConstraintValidatorContext cxt) { + LocalDate tomorrow = LocalDate.now().plusDays(1); + return dob.isBefore(tomorrow.minusYears(14)); + } +} + + diff --git a/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java b/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java new file mode 100644 index 000000000..1e37fcec5 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java @@ -0,0 +1,7 @@ +package com.makersacademy.acebook.repository; + +import com.makersacademy.acebook.model.CommentLike; +import org.springframework.data.repository.CrudRepository; + +public interface CommentLikeRepository extends CrudRepository { +} diff --git a/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java b/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java new file mode 100644 index 000000000..a9dfc802f --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java @@ -0,0 +1,7 @@ +package com.makersacademy.acebook.repository; + +import com.makersacademy.acebook.model.Comment; +import org.springframework.data.repository.CrudRepository; + +public interface CommentRepository extends CrudRepository { +} diff --git a/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java b/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java new file mode 100644 index 000000000..eaef59e5e --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java @@ -0,0 +1,7 @@ +package com.makersacademy.acebook.repository; + +import com.makersacademy.acebook.model.Friendship; +import org.springframework.data.repository.CrudRepository; + +public interface FriendshipRepository extends CrudRepository { +} diff --git a/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java b/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java new file mode 100644 index 000000000..3e3c43565 --- /dev/null +++ b/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java @@ -0,0 +1,7 @@ +package com.makersacademy.acebook.repository; + +import com.makersacademy.acebook.model.PostLike; +import org.springframework.data.repository.CrudRepository; + +public interface PostLikeRepository extends CrudRepository { +} diff --git a/src/main/resources/.DS_Store b/src/main/resources/.DS_Store new file mode 100644 index 000000000..f5a5d21ac Binary files /dev/null and b/src/main/resources/.DS_Store differ diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 865b41e1c..926b9edf4 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -3,4 +3,4 @@ spring.datasource.username= spring.datasource.password= flyway.baseline-on-migrate=true spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false -spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 699e8575a..39af65fd8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,5 @@ okta: oauth2: - issuer: https://dev-edward-andress.uk.auth0.com/ - client-id: ${OKTA_CLIENT_ID} - client-secret: ${OKTA_CLIENT_SECRET} + issuer: ${ISSUER_ACEBOOK} + client-id: ${CLIENT_ID_ACEBOOK} + client-secret: ${CLIENT_SECRET_ACEBOOK} diff --git a/src/main/resources/db/migration/V3__starting_table_design.sql b/src/main/resources/db/migration/V3__starting_table_design.sql new file mode 100644 index 000000000..95bac7a10 --- /dev/null +++ b/src/main/resources/db/migration/V3__starting_table_design.sql @@ -0,0 +1,38 @@ +ALTER table users +add email varchar(50), +add avatar varchar(100); + +ALTER table posts +add photo varchar(100), +add user_id int; + +ALTER TABLE posts +ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id); + + +CREATE table comments ( + id bigserial PRIMARY KEY, + content varchar(250), + user_id int, + post_id int, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (post_id) REFERENCES posts(id) +); + +CREATE table friendships ( +id bigserial PRIMARY KEY, +lower_user_id int, +higher_user_id int, + FOREIGN KEY (lower_user_id) REFERENCES users(id), + FOREIGN KEY (higher_user_id) REFERENCES users(id) +); + +CREATE table likes ( + id bigserial PRIMARY KEY, + user_id int, + post_id int, + comment_id int, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (post_id) REFERENCES posts(id), + FOREIGN KEY (comment_id) REFERENCES comments(id) +) \ No newline at end of file diff --git a/src/main/resources/db/migration/V4__changes_after_meeting.sql b/src/main/resources/db/migration/V4__changes_after_meeting.sql new file mode 100644 index 000000000..b8af886ff --- /dev/null +++ b/src/main/resources/db/migration/V4__changes_after_meeting.sql @@ -0,0 +1,18 @@ +ALTER table users +DROP column username, +add first_name varchar(50), +add last_name varchar(50), +add dob date; + +ALTER table posts +add post_timestamp TIMESTAMP; + + +alter table comments +add comment_timestamp TIMESTAMP; + +alter table friendships +add friendship_timestamp TIMESTAMP; + +alter table likes +add like_timestamp TIMESTAMP; \ No newline at end of file diff --git a/src/main/resources/db/migration/V5__add_username_back.sql b/src/main/resources/db/migration/V5__add_username_back.sql new file mode 100644 index 000000000..a3f2aad84 --- /dev/null +++ b/src/main/resources/db/migration/V5__add_username_back.sql @@ -0,0 +1,3 @@ +alter table users +add username varchar(50) NOT NULL UNIQUE, +drop column email; \ No newline at end of file diff --git a/src/main/resources/db/migration/V6__separate_likes.sql b/src/main/resources/db/migration/V6__separate_likes.sql new file mode 100644 index 000000000..a99ef656a --- /dev/null +++ b/src/main/resources/db/migration/V6__separate_likes.sql @@ -0,0 +1,19 @@ +drop table likes; + +create table comment_likes ( + id bigserial PRIMARY KEY, + user_id int, + comment_id int, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (comment_id) REFERENCES comments(id), + like_timestamp TIMESTAMP +); + +create table post_likes ( + id bigserial PRIMARY KEY, + user_id int, + post_id int, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (post_id) REFERENCES posts(id), + like_timestamp TIMESTAMP +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V7__seed_data.sql b/src/main/resources/db/migration/V7__seed_data.sql new file mode 100644 index 000000000..97843754f --- /dev/null +++ b/src/main/resources/db/migration/V7__seed_data.sql @@ -0,0 +1,39 @@ +ALTER SEQUENCE users_id_seq RESTART WITH 1; +ALTER SEQUENCE posts_id_seq RESTART WITH 1; +ALTER SEQUENCE comments_id_seq RESTART WITH 1; +ALTER SEQUENCE friendships_id_seq RESTART WITH 1; +ALTER SEQUENCE post_likes_id_seq RESTART WITH 1; +ALTER SEQUENCE comment_likes_id_seq RESTART WITH 1; + +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('jimmyd@gmail.com','1990-11-01','James','Dickinson','TRUE','https://imgur.com/g0Lfp8N'); +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('sashab@gmail.com','1991-06-01','Sansha','Ballom','TRUE','https://imgur.com/LZE6sj2'); +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('walterwhite@gmail.com','1972-07-10','Walter','White','TRUE','https://imgur.com/Fm5tAKH'); +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('Mandymayhem@gmail.com','1956-03-01','Mandy','Tucker','TRUE','https://imgur.com/jTQ7gAu'); +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('vicky123@gmail.com','2015-01-01','Victoria','Ballom','TRUE','https://imgur.com/2IprHCN'); +INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('yoshiboy@gmail.com','2024-03-10','Yoshi','Hunter','TRUE','https://imgur.com/a/jWApU9E'); + + +INSERT INTO posts (content,user_id,post_timestamp,photo) VALUES ('Saw a dog today!',1,'2024-01-01 12:00:00','https://imgur.com/a/tSWP7DS'); +INSERT INTO posts (content,user_id,post_timestamp) VALUES ('Going out to cook, noone follow me',3,'2024-01-03 18:00:00'); +INSERT INTO posts (content,user_id,post_timestamp) VALUES ('How do I use this?',4,'2024-01-04 12:00:00'); +INSERT INTO posts (content,user_id,post_timestamp) VALUES ('How to use',4,'2024-01-04 12:04:00'); +INSERT INTO posts (content,user_id,post_timestamp) VALUES ('Roast beef casserole recipe',4,'2024-01-04 15:04:00'); + +INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('It''s so cute!',2,1,'2024-01-01 12:30:00'); +INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('It is! <3 <3',1,1,'2024-01-01 12:45:00'); +INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('Where are you going?',5,2,'2024-01-03 18:03:00'); +INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('None of your business',3,2,'2024-01-03 18:05:00'); +INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('Don''t talk to my daughter like that!',2,2,'2024-01-03 18:07:00'); + +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (1,2,'2023-09-01 08:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (2,5,'2023-06-01 10:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (3,4,'2022-01-01 12:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (1,6,'2024-03-11 12:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (2,6,'2024-03-11 12:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (3,6,'2024-03-11 12:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (4,6,'2024-03-11 12:00:00'); +INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (5,6,'2024-03-11 12:00:00'); + +INSERT INTO post_likes(user_id,post_id,like_timestamp) values (2,1,'2024-01-01 12:27:00'); + +INSERT INTO comment_likes(user_id,comment_id,like_timestamp) values (1,1,'2024-01-01 12:42:00'); \ No newline at end of file diff --git a/src/main/resources/static/images/placeholders/alan_partridge.jpg b/src/main/resources/static/images/placeholders/alan_partridge.jpg new file mode 100644 index 000000000..e818b7d9a Binary files /dev/null and b/src/main/resources/static/images/placeholders/alan_partridge.jpg differ diff --git a/src/main/resources/static/images/userAvatars/9.jpg b/src/main/resources/static/images/userAvatars/9.jpg new file mode 100644 index 000000000..e818b7d9a Binary files /dev/null and b/src/main/resources/static/images/userAvatars/9.jpg differ diff --git a/src/main/resources/static/main.css b/src/main/resources/static/main.css index d0260873c..ed95dc396 100644 --- a/src/main/resources/static/main.css +++ b/src/main/resources/static/main.css @@ -8,3 +8,17 @@ text-align: left; margin-bottom: 0.5rem;; } + +.user-initials { + width: 32px; + height: 32px; + border-radius: 50%; + background-color: #1877f2; + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + font-size: 14px; + margin-left: 12px; +} \ No newline at end of file diff --git a/src/main/resources/templates/.DS_Store b/src/main/resources/templates/.DS_Store new file mode 100644 index 000000000..06be266d5 Binary files /dev/null and b/src/main/resources/templates/.DS_Store differ diff --git a/src/main/resources/templates/friends.html b/src/main/resources/templates/friends.html new file mode 100644 index 000000000..c51c3857c --- /dev/null +++ b/src/main/resources/templates/friends.html @@ -0,0 +1,53 @@ + + + + + + Account Settings + + + + + + + + + + +

Friends List

+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 000000000..f5ee5f009 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,65 @@ + + + + + + Home + + + + + + + +

Posts

+ +
+ Signed in as +
+ +
+

Content:

+ +

+
+ +
    +
  • +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/newUser.html b/src/main/resources/templates/newUser.html new file mode 100644 index 000000000..599186594 --- /dev/null +++ b/src/main/resources/templates/newUser.html @@ -0,0 +1,79 @@ + + + + + Acebook + + + + + + +
+ + +
+
+
+ + +
+
+ +
+
+ + + + + + +
+
+ +
+
+
+ + + + + + + + + +
+
+
+ + + + + + + + + + + + diff --git a/src/main/resources/templates/posts/index.html b/src/main/resources/templates/posts/index.html deleted file mode 100644 index b5ef169f1..000000000 --- a/src/main/resources/templates/posts/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Acebook - - - - -

Posts

- -
- Signed in as -
- -
-

Content:

-

-
- -
    -
  • -
- - - diff --git a/src/main/resources/templates/profile.html b/src/main/resources/templates/profile.html new file mode 100644 index 000000000..aa7babcb1 --- /dev/null +++ b/src/main/resources/templates/profile.html @@ -0,0 +1,53 @@ + + + + + + Account Settings + + + + + + + + + + +

Profile

+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/settings.html b/src/main/resources/templates/settings.html new file mode 100644 index 000000000..aa781b7c8 --- /dev/null +++ b/src/main/resources/templates/settings.html @@ -0,0 +1,116 @@ + + + + + + Account Settings + + + + + + + + + +
+ +
+ +
+ Early Alan +
+
+ + +
+ + +
+
+ + +

Invalid data

+
+ +
+ + +

Invalid data

+
+
+ +
+
+ + +
+
+ + +

Invalid data

+
+
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+
+ + + \ No newline at end of file diff --git a/src/test/java/com/makersacademy/acebook/feature/SignUpTest.java b/src/test/java/com/makersacademy/acebook/feature/SignUpTest.java index dcb1416bb..131def3b5 100644 --- a/src/test/java/com/makersacademy/acebook/feature/SignUpTest.java +++ b/src/test/java/com/makersacademy/acebook/feature/SignUpTest.java @@ -7,7 +7,13 @@ import org.junit.Test; 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.FluentWait; +import org.openqa.selenium.support.ui.Wait; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; public class SignUpTest { @@ -16,7 +22,7 @@ public class SignUpTest { @Before public void setup() { - System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver"); + System.setProperty("webdriver.chrome.driver", "/opt/homebrew/bin/chromedriver"); driver = new ChromeDriver(); faker = new Faker(); } @@ -36,7 +42,13 @@ public void successfulSignUpAlsoLogsInUser() { driver.findElement(By.name("password")).sendKeys("P@55qw0rd"); driver.findElement(By.name("action")).click(); driver.findElement(By.name("action")).click(); + + // If getting a 'StaleElementReferenceException' may need to extend wait time. + driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2)); + String greetingText = driver.findElement(By.id("greeting")).getText(); Assert.assertEquals("Signed in as " + email, greetingText); } + + }