From 8c7966deb20cb682df284ab5d6737bbf93bf9c3a Mon Sep 17 00:00:00 2001 From: Guams Date: Sat, 4 Jan 2025 18:17:08 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20commentaires=20(CRUD=20pas=20enti?= =?UTF-8?q?=C3=A8rement=20impl=C3=A9ment=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/SpringSecurityConfig.java | 5 +- .../review/controller/AuthorController.java | 9 +-- .../review/controller/CommentController.java | 79 +++++++++++++++++++ .../review/controller/PostController.java | 22 ++++-- .../guams/review/model/AuthorRepository.java | 3 +- .../guams/review/model/CommentRepository.java | 27 +++++++ .../com/guams/review/model/dao/Author.java | 4 - .../com/guams/review/model/dao/Comment.java | 26 ++++++ .../guams/review/model/dao/CommentIds.java | 25 ++++++ .../java/com/guams/review/model/dao/Post.java | 2 + .../guams/review/service/AuthorService.java | 11 +-- .../guams/review/service/CommentService.java | 42 ++++++++++ src/main/resources/script.sql | 29 +++++-- 13 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/guams/review/controller/CommentController.java create mode 100644 src/main/java/com/guams/review/model/CommentRepository.java create mode 100644 src/main/java/com/guams/review/model/dao/Comment.java create mode 100644 src/main/java/com/guams/review/model/dao/CommentIds.java create mode 100644 src/main/java/com/guams/review/service/CommentService.java diff --git a/src/main/java/com/guams/review/configuration/SpringSecurityConfig.java b/src/main/java/com/guams/review/configuration/SpringSecurityConfig.java index 4f341b6..7801301 100644 --- a/src/main/java/com/guams/review/configuration/SpringSecurityConfig.java +++ b/src/main/java/com/guams/review/configuration/SpringSecurityConfig.java @@ -38,9 +38,10 @@ public class SpringSecurityConfig { "/api/authors/{id}", "/api/authors/{id}/posts", "/api/posts", - "/api/posts/{id}").permitAll() // Autorise les GET sur ces routes + "/api/posts/{id}", + "/api/comments/posts/{id}", + "/api/comments").permitAll() // Autorise les GET sur ces routes .requestMatchers("/api/authors/login", "/api/authors/register").permitAll() // Autorise sans authentification - .requestMatchers("/api/authors/me").authenticated() // Requiert authentification .anyRequest().authenticated() // Toutes les autres routes nécessitent une authentification ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Ajoute le filtre JWT diff --git a/src/main/java/com/guams/review/controller/AuthorController.java b/src/main/java/com/guams/review/controller/AuthorController.java index 7f1bae7..1c05303 100644 --- a/src/main/java/com/guams/review/controller/AuthorController.java +++ b/src/main/java/com/guams/review/controller/AuthorController.java @@ -98,7 +98,7 @@ public class AuthorController { @PostMapping("/register") public ResponseEntity authorRegister(@RequestBody Author author) { Assert.isNull(author.getId(), "Author id must be null"); - if (authorRepository.findByName(author.getName()) != null) { + if (authorService.findByName(author.getName()).isPresent()) { throw new AlreadyExistsException("Author already exists"); } author.setPassword(passwordEncoder.encode(author.getPassword())); @@ -112,12 +112,7 @@ public class AuthorController { throw new ForbiddenExecption("You are not authorized to access this resource"); } - String username = authentication.getName(); - Author author = authorRepository.findByName(username); - - if (author == null) { - throw new NotFoundException("Author not found"); - } + Author author = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); return author.setPassword(""); } diff --git a/src/main/java/com/guams/review/controller/CommentController.java b/src/main/java/com/guams/review/controller/CommentController.java new file mode 100644 index 0000000..5e05d7d --- /dev/null +++ b/src/main/java/com/guams/review/controller/CommentController.java @@ -0,0 +1,79 @@ +package com.guams.review.controller; + +import com.guams.review.exception.ForbiddenExecption; +import com.guams.review.exception.NotFoundException; +import com.guams.review.model.AuthorRepository; +import com.guams.review.model.dao.Author; +import com.guams.review.model.dao.Comment; +import com.guams.review.model.dao.CommentIds; +import com.guams.review.service.AuthorService; +import com.guams.review.service.CommentService; +import com.guams.review.service.PostService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.List; + +@RestController +@RequestMapping(path = "api/comments") +@RequiredArgsConstructor +public class CommentController { + + private final CommentService commentService; + private final AuthorRepository authorRepository; + private final PostService postService; + private final AuthorService authorService; + + @GetMapping + public List listAllComments() { + return commentService.list(); + } + + @PostMapping("/posts/{id}") + public ResponseEntity addComment(@RequestBody Comment comment, Authentication authentication, @PathVariable("id") Long postId) { + if (authentication == null || !authentication.isAuthenticated()) { + throw new ForbiddenExecption("You are not authorized to access this resource"); + } + + Author author = authorRepository.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); + Comment insertedComment = commentService.insert(comment + .setCommentDate(Timestamp.from(Instant.now())) + .setIsUpdated(false)); + postService.findById(postId).orElseThrow(() -> new NotFoundException("Post not found")); + + commentService.associateCommentToPostAndAuthor(author.getId(), postId, insertedComment.getId()); + return new ResponseEntity<>(insertedComment, HttpStatus.CREATED); + } + + @GetMapping("/posts/{id}") + public List listCommentsByPostId(@PathVariable("id") Long postId) { + postService.findById(postId).orElseThrow(() -> new NotFoundException("Post not found")); + return commentService.getCommentsByCommentId(postId); + } + + @PutMapping("/{id}") + public void updateComment(@PathVariable Long id, @RequestBody CommentIds commentIds, Authentication authentication) { + if (authentication == null || !authentication.isAuthenticated()) { + throw new ForbiddenExecption("You are not authorized to access this resource"); + } + + Author author = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); + Comment commentToUpdate = commentService.findById(id).orElseThrow(() -> new NotFoundException("Comment not found")); + CommentIds concernedCommentIds = commentService.getCommentIdsByCommentId(id).orElseThrow(() -> new NotFoundException("Comment not found")); + if (!author.getId().equals(concernedCommentIds.getAuthorId())) { + throw new ForbiddenExecption("You are not authorized to access this resource"); + } + + commentService.insert(commentToUpdate + .setIsUpdated(true) + .setContent(commentIds.getContent())); + } + +// @DeleteMapping("/{id}") +// public void deleteComment(@PathVariable Long id, Authentication authentication) {} +} diff --git a/src/main/java/com/guams/review/controller/PostController.java b/src/main/java/com/guams/review/controller/PostController.java index 987d207..9f0efc2 100644 --- a/src/main/java/com/guams/review/controller/PostController.java +++ b/src/main/java/com/guams/review/controller/PostController.java @@ -2,8 +2,8 @@ package com.guams.review.controller; import com.guams.review.exception.ForbiddenExecption; import com.guams.review.exception.NotFoundException; -import com.guams.review.model.AuthorRepository; import com.guams.review.model.dao.Author; +import com.guams.review.model.dao.Comment; import com.guams.review.model.dao.Post; import com.guams.review.service.AuthorService; import com.guams.review.service.PostService; @@ -19,6 +19,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.sql.Timestamp; import java.time.Instant; +import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor @@ -28,7 +29,6 @@ public class PostController { private final PostService postService; private final AuthorService authorService; - private final AuthorRepository authorRepository; @GetMapping public List listPosts() { @@ -45,14 +45,15 @@ public class PostController { if (authentication == null) { throw new ForbiddenExecption("You have to login to do that"); } - Author authenticatedAuthor = authorRepository.findByName(authentication.getName()); + Author authenticatedAuthor = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); //Si l'user authent possède ce post if (authorService.listPublicationOfAuthor(authenticatedAuthor.getId()).stream().map(Post::getId).toList().contains(id)) { Post postToUpdate = postService.findById(id).orElseThrow(() -> new NotFoundException("Post not found")); postService.insert(updatedPost .setId(postToUpdate.getId()) .setIllustration(postToUpdate.getIllustration()) - .setPublicationDate(postToUpdate.getPublicationDate())); + .setPublicationDate(postToUpdate.getPublicationDate()) + .setIsUpdated(true)); } else { throw new ForbiddenExecption("You do not have permission to update this post"); } @@ -63,7 +64,7 @@ public class PostController { if (authentication == null) { throw new ForbiddenExecption("You have to login to do that"); } - Author authenticatedAuthor = authorRepository.findByName(authentication.getName()); + Author authenticatedAuthor = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); if (authorService.listPublicationOfAuthor(authenticatedAuthor.getId()).stream().map(Post::getId).toList().contains(id)) { Post postToUpdate = postService.findById(id).orElseThrow(() -> new NotFoundException("Post not found")); postService.insert(postToUpdate.setIllustration(illustration.getBytes())); @@ -78,7 +79,14 @@ public class PostController { if (authentication == null) { throw new ForbiddenExecption("You have to login to do that"); } - return new ResponseEntity<>(postService.insert(postToCreate.setPublicationDate(Timestamp.from(Instant.now()))), HttpStatus.CREATED); + return new ResponseEntity<>(postService.insert(postToCreate + .setPublicationDate(Timestamp.from(Instant.now())) + .setIsUpdated(false)), HttpStatus.CREATED); + } + + @GetMapping("/{id}/comments") + public List listCommentsByPostId(@PathVariable Long id) { + return new ArrayList<>(); } @DeleteMapping("{id}") @@ -86,7 +94,7 @@ public class PostController { if (authentication == null) { throw new ForbiddenExecption("You have to login to do that"); } - Author authenticatedAuthor = authorRepository.findByName(authentication.getName()); + Author authenticatedAuthor = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found")); if (authorService.listPublicationOfAuthor(authenticatedAuthor.getId()).stream().map(Post::getId).toList().contains(id)) { Post postToDelete = postService.findById(id).orElseThrow(() -> new NotFoundException("Post not found")); postService.delete(authenticatedAuthor.getId(), postToDelete.getId()); diff --git a/src/main/java/com/guams/review/model/AuthorRepository.java b/src/main/java/com/guams/review/model/AuthorRepository.java index 607d6f3..bc1f02c 100644 --- a/src/main/java/com/guams/review/model/AuthorRepository.java +++ b/src/main/java/com/guams/review/model/AuthorRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import java.util.UUID; @Repository @@ -22,5 +23,5 @@ public interface AuthorRepository extends CrudRepository { List findAll(); - Author findByName(String name); + Optional findByName(String name); } diff --git a/src/main/java/com/guams/review/model/CommentRepository.java b/src/main/java/com/guams/review/model/CommentRepository.java new file mode 100644 index 0000000..a306253 --- /dev/null +++ b/src/main/java/com/guams/review/model/CommentRepository.java @@ -0,0 +1,27 @@ +package com.guams.review.model; + +import com.guams.review.model.dao.Comment; +import com.guams.review.model.dao.CommentIds; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface CommentRepository extends CrudRepository { + + List findAll(); + + @Query("select post_fk, comment_fk, author_fk from comment_association where comment_fk = :commentId") + Optional getCommentIdsByCommentId(Long commentId); + + @Query("select distinct comment.id, comment.content, comment.comment_date, comment.is_updated from comment join comment_association on " + + "comment_association.post_fk = :commentId") + List getCommentsByCommentId(Long commentId); + + @Modifying + @Query("insert into comment_association(comment_fk, post_fk, author_fk) values (:commentId, :postId, :authorId);") + void associateCommentToUserAndPost(UUID authorId, Long postId, Long commentId); +} diff --git a/src/main/java/com/guams/review/model/dao/Author.java b/src/main/java/com/guams/review/model/dao/Author.java index b783802..bdfef7f 100644 --- a/src/main/java/com/guams/review/model/dao/Author.java +++ b/src/main/java/com/guams/review/model/dao/Author.java @@ -7,11 +7,7 @@ import lombok.experimental.Accessors; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import java.util.Collection; -import java.util.List; import java.util.UUID; diff --git a/src/main/java/com/guams/review/model/dao/Comment.java b/src/main/java/com/guams/review/model/dao/Comment.java new file mode 100644 index 0000000..7ae4e91 --- /dev/null +++ b/src/main/java/com/guams/review/model/dao/Comment.java @@ -0,0 +1,26 @@ +package com.guams.review.model.dao; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +import java.sql.Timestamp; + +@Getter +@Setter +@Accessors(chain = true) + +@Table(name = "comment") +public class Comment { + @Id + Long id; + @Column("content") + String content; + @Column("comment_date") + Timestamp commentDate; + @Column("is_updated") + Boolean isUpdated; +} diff --git a/src/main/java/com/guams/review/model/dao/CommentIds.java b/src/main/java/com/guams/review/model/dao/CommentIds.java new file mode 100644 index 0000000..3d2f53e --- /dev/null +++ b/src/main/java/com/guams/review/model/dao/CommentIds.java @@ -0,0 +1,25 @@ +package com.guams.review.model.dao; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +import java.util.UUID; + +@Getter +@Setter +@Accessors(chain = true) +@Table("comment_association") +public class CommentIds { + + @Id + Long postId; + @Column("comment_fk") + Long commentId; + @Column("author_fk") + UUID authorId; + String content; +} diff --git a/src/main/java/com/guams/review/model/dao/Post.java b/src/main/java/com/guams/review/model/dao/Post.java index 455072a..991087c 100644 --- a/src/main/java/com/guams/review/model/dao/Post.java +++ b/src/main/java/com/guams/review/model/dao/Post.java @@ -28,5 +28,7 @@ public class Post { String category; @Column("publication_date") Timestamp publicationDate; + @Column("is_updated") + Boolean isUpdated; } diff --git a/src/main/java/com/guams/review/service/AuthorService.java b/src/main/java/com/guams/review/service/AuthorService.java index db54a7e..108080d 100644 --- a/src/main/java/com/guams/review/service/AuthorService.java +++ b/src/main/java/com/guams/review/service/AuthorService.java @@ -59,23 +59,24 @@ public class AuthorService implements UserDetailsService } Author author = findById(id).orElseThrow(() -> new NotFoundException("Author not found")); String username = authentication.getName(); - Author authorAuthenticated = authorRepository.findByName(username); + Author authorAuthenticated = authorRepository.findByName(username).orElseThrow(() -> new NotFoundException("Author not found")); if (authorAuthenticated.getId().compareTo(author.getId()) != 0 && !authorAuthenticated.getRole().equals("ADMIN")) { throw new ForbiddenExecption("Specified Author is not authorized to do that"); } return author; } + public Optional findByName(String name) { + return authorRepository.findByName(name); + } + public void delete(Author author) { authorRepository.delete(author); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Author author = authorRepository.findByName(username); - if (author == null) { - throw new UsernameNotFoundException(username); - } + Author author = authorRepository.findByName(username).orElseThrow(() -> new UsernameNotFoundException("Author not found")); return new User(author.getName(), author.getPassword(), Collections.singletonList(new SimpleGrantedAuthority(author.getRole()))); } } diff --git a/src/main/java/com/guams/review/service/CommentService.java b/src/main/java/com/guams/review/service/CommentService.java new file mode 100644 index 0000000..d5cb6ce --- /dev/null +++ b/src/main/java/com/guams/review/service/CommentService.java @@ -0,0 +1,42 @@ +package com.guams.review.service; + +import com.guams.review.model.CommentRepository; +import com.guams.review.model.dao.Comment; +import com.guams.review.model.dao.CommentIds; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + + public Optional findById(Long id) { + return commentRepository.findById(id); + } + + public List list() { + return commentRepository.findAll(); + } + + public List getCommentsByCommentId(Long commentId) { + return commentRepository.getCommentsByCommentId(commentId); + } + + public void associateCommentToPostAndAuthor(UUID authorId, Long postId, Long commentId) { + commentRepository.associateCommentToUserAndPost(authorId, postId, commentId); + } + public Optional getCommentIdsByCommentId(Long id) { + return commentRepository.getCommentIdsByCommentId(id); + } + + public Comment insert(Comment comment) { + return commentRepository.save(comment); + } + +} \ No newline at end of file diff --git a/src/main/resources/script.sql b/src/main/resources/script.sql index 382927b..65ed7b0 100644 --- a/src/main/resources/script.sql +++ b/src/main/resources/script.sql @@ -1,9 +1,8 @@ +drop table if exists comment_association; +drop table if exists comment; drop table if exists publication; drop table if exists post; drop table if exists author; -drop type if exists author_role; - -create type author_role as enum ('READER', 'WRITER', 'ADMIN'); create table author ( @@ -11,7 +10,7 @@ create table author name varchar(255) unique not null, password text not null, profile_picture bytea, - role author_role default 'READER' not null + role varchar(10) default 'READER' not null ); create table post @@ -22,7 +21,8 @@ create table post title varchar(50) not null, body text not null, category varchar(50) not null, - publication_date timestamp not null + publication_date timestamp not null, + is_updated boolean default false ); create table publication @@ -32,3 +32,22 @@ create table publication foreign key (author_fk) references author (id), foreign key (post_fk) references post (id) ); + +create table comment +( + id serial primary key, + content varchar(512) not null, + comment_date timestamp not null, + is_updated boolean default false +); + +create table comment_association +( + ca_pk serial primary key, + comment_fk serial, + post_fk serial, + author_fk uuid, + foreign key (comment_fk) references comment (id), + foreign key (post_fk) references post (id), + foreign key (author_fk) references author (id) +); \ No newline at end of file