Ajout de commentaires (CRUD pas entièrement implémenté)

This commit is contained in:
Guams 2025-01-04 18:17:08 +01:00
parent 02ea44d57d
commit 8c7966deb2
13 changed files with 253 additions and 31 deletions

View File

@ -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

View File

@ -98,7 +98,7 @@ public class AuthorController {
@PostMapping("/register")
public ResponseEntity<Author> 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("");
}

View File

@ -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<Comment> listAllComments() {
return commentService.list();
}
@PostMapping("/posts/{id}")
public ResponseEntity<Comment> 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<Comment> 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) {}
}

View File

@ -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<Post> 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<Comment> 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());

View File

@ -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<Author, UUID> {
List<Author> findAll();
Author findByName(String name);
Optional<Author> findByName(String name);
}

View File

@ -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<Comment, Long> {
List<Comment> findAll();
@Query("select post_fk, comment_fk, author_fk from comment_association where comment_fk = :commentId")
Optional<CommentIds> 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<Comment> 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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -28,5 +28,7 @@ public class Post {
String category;
@Column("publication_date")
Timestamp publicationDate;
@Column("is_updated")
Boolean isUpdated;
}

View File

@ -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<Author> 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())));
}
}

View File

@ -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<Comment> findById(Long id) {
return commentRepository.findById(id);
}
public List<Comment> list() {
return commentRepository.findAll();
}
public List<Comment> getCommentsByCommentId(Long commentId) {
return commentRepository.getCommentsByCommentId(commentId);
}
public void associateCommentToPostAndAuthor(UUID authorId, Long postId, Long commentId) {
commentRepository.associateCommentToUserAndPost(authorId, postId, commentId);
}
public Optional<CommentIds> getCommentIdsByCommentId(Long id) {
return commentRepository.getCommentIdsByCommentId(id);
}
public Comment insert(Comment comment) {
return commentRepository.save(comment);
}
}

View File

@ -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)
);