Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Backend/enhancement/forum post endpoints#405 #435

Merged
merged 3 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
package com.gamelounge.backend.controller
//
//import org.springframework.web.bind.annotation.RequestMapping
//import org.springframework.web.bind.annotation.RestController
//
//@RestController
//@RequestMapping("/post")
//class PostController(
// private lateinit var postService: PostService
//) {
// fun getPostByUsername(username: String){
//
// }
//}

import com.gamelounge.backend.entity.Post
import com.gamelounge.backend.service.PostService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.util.UUID

@RestController
@RequestMapping("/posts")
class PostController(private val postService: PostService) {

@PostMapping
fun createPost(@CookieValue("SESSIONID") sessionId: UUID, @RequestBody post: Post): ResponseEntity<Post> {
val newPost = postService.createPost(sessionId, post)
return ResponseEntity.ok(newPost)
}

@GetMapping("/{id}")
fun getPost(@PathVariable id: Long): ResponseEntity<Post> {
val post = postService.getPost(id)
return ResponseEntity.ok(post)
}

@PutMapping("/{id}")
fun updatePost(@CookieValue("SESSIONID") sessionId: UUID, @PathVariable id: Long, @RequestBody updatedPost: Post): ResponseEntity<Post> {
val post = postService.updatePost(sessionId, id, updatedPost)
return ResponseEntity.ok(post)
}

@DeleteMapping("/{id}")
fun deletePost(@CookieValue("SESSIONID") sessionId: UUID, @PathVariable id: Long): ResponseEntity<Void> {
postService.deletePost(sessionId, id)
return ResponseEntity.noContent().build<Void>()
}

@GetMapping
fun getAllPosts(): ResponseEntity<List<Post>> {
val posts = postService.getAllPosts()
return ResponseEntity.ok(posts)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ import java.time.Instant
class Comment(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val commentId: Long = 0,
var commentId: Long = 0,

val content: String = "",
val creationDate: Instant = Instant.now(),
val upvotes: Int = 0,
val downvotes: Int = 0,
var content: String = "",
var creationDate: Instant = Instant.now(),
var upvotes: Int = 0,
var downvotes: Int = 0,


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "postId")
val post: Post? = null,
var post: Post? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "lfgId")
val lfg: LFG? = null,
var lfg: LFG? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userId")
val user: User? = null
var user: User? = null
)
25 changes: 14 additions & 11 deletions app/backend/src/main/kotlin/com/gamelounge/backend/entity/Post.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@ package com.gamelounge.backend.entity
import jakarta.persistence.*
import lombok.NoArgsConstructor
import java.time.Instant

enum class PostCategory {
GUIDE, REVIEW, DISCUSSION
}
@Entity
@Table(name = "posts")
@NoArgsConstructor
class Post(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val postId: Long = 0,
var postId: Long = 0,

val content: String = "",
val creationDate: Instant = Instant.now(),
val upvotes: Int = 0,
val downvotes: Int = 0,
var title: String = "",
var content: String = "",
var creationDate: Instant = Instant.now(),
var upvotes: Int = 0,
var downvotes: Int = 0,

val category: String = "",
val annotations: String? = "",
@Enumerated(EnumType.STRING)
var category: PostCategory = PostCategory.DISCUSSION,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userId")
val user: User? = null,
var user: User? = null,

@OneToMany(mappedBy = "post", cascade = [CascadeType.ALL], orphanRemoval = true)
val comments: List<Comment> = mutableListOf(),
var comments: List<Comment> = mutableListOf(),

@ManyToOne
@JoinColumn(name = "gameId")
val relatedGame: Game? = null
var relatedGame: Game? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ class UsernameAlreadyExistException(message: String): RuntimeException(message)
class EmailNotFoundException(message: String): RuntimeException(message)
class TokenNotFoundException(message: String): RuntimeException(message)
class PasswordMismatchException(message: String): RuntimeException(message)
class UserNotFoundException(message: String): RuntimeException(message)
class PostNotFoundException(message: String) : RuntimeException(message)
class UnauthorizedPostAccessException(message: String) : RuntimeException(message)
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,24 @@ class GlobalExceptionHandler {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(mapOf("errorMessage" to exception.message))
}

@ExceptionHandler(UserNotFoundException::class)
fun handleUserNotFoundException(exception: UserNotFoundException): ResponseEntity<Map<String, String?>> {
logger.info(exception.message)
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(mapOf("errorMessage" to exception.message))
}
@ExceptionHandler(PostNotFoundException::class)
fun handlePostNotFoundException(exception: PostNotFoundException): ResponseEntity<Map<String, String?>> {
logger.info(exception.message)
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(mapOf("errorMessage" to exception.message))
}
@ExceptionHandler(UnauthorizedPostAccessException::class)
fun handleUnauthorizedPostAccessException(exception: UnauthorizedPostAccessException): ResponseEntity<Map<String, String?>> {
logger.info(exception.message)
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(mapOf("errorMessage" to exception.message))
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.gamelounge.backend.middleware

import com.gamelounge.backend.repository.SessionRepository
import com.gamelounge.backend.exception.SessionNotFoundException
import org.springframework.stereotype.Service
import java.util.UUID

@Service
class SessionAuth(private val sessionRepository: SessionRepository) {

fun getUserIdFromSession(sessionId: UUID): Long {
val session = sessionRepository.findById(sessionId).orElseThrow {
SessionNotFoundException("Session not found for ID: $sessionId")
}
return session.user.userId
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.gamelounge.backend.repository

import org.springframework.stereotype.Repository
import com.gamelounge.backend.entity.Post
import com.gamelounge.backend.entity.User
import org.springframework.data.repository.CrudRepository

interface PostRepository: CrudRepository<Post, Long> {
import org.springframework.data.jpa.repository.JpaRepository
@Repository
interface PostRepository : JpaRepository<Post, Long>{

fun findByUser(user: User): List<Post>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.gamelounge.backend.service

import com.gamelounge.backend.entity.Post
import com.gamelounge.backend.exception.PostNotFoundException
import com.gamelounge.backend.exception.UnauthorizedPostAccessException
import com.gamelounge.backend.exception.UsernameNotFoundException
import com.gamelounge.backend.repository.PostRepository
import com.gamelounge.backend.middleware.SessionAuth
import com.gamelounge.backend.repository.UserRepository
import org.springframework.stereotype.Service
import java.util.UUID

@Service
class PostService(
private val postRepository: PostRepository,
private val sessionAuth: SessionAuth,
private val userRepository: UserRepository
) {
fun createPost(sessionId: UUID, post: Post): Post {
val userId = sessionAuth.getUserIdFromSession(sessionId)
val user = userRepository.findById(userId).orElseThrow { UsernameNotFoundException("User not found") }
post.user = user
return postRepository.save(post)
}

fun getPost(postId: Long): Post {
return postRepository.findById(postId).orElseThrow { PostNotFoundException("Post not found with ID: $postId") }
}

fun updatePost(sessionId: UUID, postId: Long, updatedPost: Post): Post {
val userId = sessionAuth.getUserIdFromSession(sessionId)
val post = getPost(postId)

if (post.user?.userId != userId) {
throw UnauthorizedPostAccessException("Unauthorized to update post with ID: $postId")
}

post.title = updatedPost.title
post.content = updatedPost.content
post.category = updatedPost.category
// TODO

return postRepository.save(post)
}

fun deletePost(sessionId: UUID, postId: Long) {
val userId = sessionAuth.getUserIdFromSession(sessionId)
val post = getPost(postId)

if (post.user?.userId != userId) {
throw UnauthorizedPostAccessException("Unauthorized to delete post with ID: $postId")
}

postRepository.delete(post)
}

fun getAllPosts(): List<Post> {
return postRepository.findAll()
}
}