Skip to content

Commit

Permalink
feat: post
Browse files Browse the repository at this point in the history
  • Loading branch information
siwonpada committed Aug 25, 2024
1 parent 70b25fc commit 4c93a60
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.gistory.newbies_server_24.configurations
import me.gistory.newbies_server_24.providers.TokenProvider
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
Expand Down Expand Up @@ -31,6 +32,7 @@ class SecurityConfiguration {
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.authorizeHttpRequests { req ->
req.requestMatchers(HttpMethod.GET).permitAll()
req.requestMatchers("/swagger-ui/**" , "v3/api-docs/**").permitAll()
req.requestMatchers("/auth/login", "/auth/register").permitAll()
req.requestMatchers("/error").permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import java.util.*

@Tag(name="Board")
@RestController
@SecurityRequirement(name = "Bearer Authorization")
@RequestMapping("/boards")
class BoardController(private val boardService: BoardService) {

Expand All @@ -26,6 +25,7 @@ class BoardController(private val boardService: BoardService) {
)
}

@SecurityRequirement(name = "Bearer Authorization")
@PostMapping("")
fun createBoard(
authentication: Authentication,
Expand All @@ -34,6 +34,7 @@ class BoardController(private val boardService: BoardService) {
return boardService.createBoard(body, authentication.name).toSummaryDto();
}

@SecurityRequirement(name = "Bearer Authorization")
@DeleteMapping("{uuid}")
fun deleteBoard(
authentication: Authentication,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package me.gistory.newbies_server_24.controllers

import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import me.gistory.newbies_server_24.dto.CreatePostDto
import me.gistory.newbies_server_24.dto.PostDto
import me.gistory.newbies_server_24.dto.PostListDto
import me.gistory.newbies_server_24.dto.UpdatePostDto
import me.gistory.newbies_server_24.entities.Post
import me.gistory.newbies_server_24.services.PostService
import org.springframework.security.core.Authentication
import org.springframework.web.bind.annotation.*
import java.util.*

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

@GetMapping("")
fun getPosts(
@RequestParam(value = "tag", required = false) tag: String?,
@RequestParam(value = "boardUuid", required = false) boardUuid: String?,
): PostListDto {
val boardId = boardUuid?.let { UUID.fromString(it) }
val posts = postService.getPosts(boardId = boardId, tagId = tag).toList()
return PostListDto(
count = posts.size,
list = posts.map(Post::toPostDto)
)
}

@GetMapping("/search")
fun searchPosts(
@RequestParam(value = "keyword") keyword: String
): PostListDto {
val posts = postService.searchPost(keyword).toList()
return PostListDto(
count = posts.size,
list = posts.map(Post::toPostDto)
)
}

@SecurityRequirement(name = "Bearer Authorization")
@PostMapping("")
fun createPost(
@RequestBody post: CreatePostDto,
@RequestParam(value = "boardUuid") boardUuid: String,
authentication: Authentication
): PostDto {
return postService.createPost(post, UUID.fromString(boardUuid), authentication.name).toPostDto()
}

@SecurityRequirement(name = "Bearer Authorization")
@PatchMapping("/{uuid}")
fun updatePost(
@RequestBody post: UpdatePostDto,
@PathVariable("uuid") uuid: String,
authentication: Authentication
): PostDto {
return postService.updatePost(post, UUID.fromString(uuid), authentication.name).toPostDto()
}

@SecurityRequirement(name = "Bearer Authorization")
@DeleteMapping("/{uuid}")
fun deletePost(
@PathVariable("uuid") uuid: String,
authentication: Authentication
): Unit {
return postService.deletePost(UUID.fromString(uuid), authentication.name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.*

@Tag(name = "Tag")
@RestController
@SecurityRequirement(name = "Bearer Authorization")
@RequestMapping("/tag")
class TagController (
private val tagService: TagService,
Expand All @@ -34,6 +33,7 @@ class TagController (
)
}

@SecurityRequirement(name = "Bearer Authorization")
@PostMapping("")
fun createTag(@RequestBody tag: TagDto): TagDto {
return tagService.createTag(tag.key).toTagDto()
Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/me/gistory/newbies_server_24/dto/PostDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package me.gistory.newbies_server_24.dto

import me.gistory.newbies_server_24.entities.Board
import java.util.*

data class CreatePostDto(
val title: String,
val body: String,
val tags: List<String>
)

data class UpdatePostDto(
val title: String?,
val body: String?,
val tags: List<String>?,
)

data class PostDto (
val id: UUID,
val title: String,
val body: String,
val tags: List<String>,
val board: BoardSummaryDto,
val createdAt: Date,
val createdBy: UserDto
)

data class PostListDto (
val count: Int,
val list: List<PostDto>,
)
19 changes: 16 additions & 3 deletions src/main/kotlin/me/gistory/newbies_server_24/entities/Post.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package me.gistory.newbies_server_24.entities

import jakarta.persistence.*
import me.gistory.newbies_server_24.dto.PostDto
import org.hibernate.annotations.CreationTimestamp
import java.util.*

@Entity
@Table(name = "posts")
class Post(
@Column() val title: String,
@Column() val body: String,
@Column() var title: String,
@Column() var body: String,

@ManyToOne
@JoinColumn(name = "created_by")
val createdBy: User,

@ManyToMany
@JoinTable(name = "post_to_tag")
val tags: MutableSet<Tag>,
var tags: MutableSet<Tag>,

@ManyToOne
@JoinColumn(name = "board_id")
Expand All @@ -29,4 +30,16 @@ class Post(
@Column(nullable = false)
@CreationTimestamp
val createdAt: Date = Date()

fun toPostDto(): PostDto {
return PostDto(
id = id,
title = title,
body = body,
tags = tags.toList().map { it.key },
board = board.toSummaryDto(),
createdAt = createdAt,
createdBy = createdBy.toDto()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.gistory.newbies_server_24.exceptions

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Post Not Found")
class PostNotFoundException : RuntimeException()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.gistory.newbies_server_24.exceptions

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ResponseStatus

@ResponseStatus(HttpStatus.NOT_FOUND, reason = "Tag Not Found")
class TagNotFoundException : RuntimeException()
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package me.gistory.newbies_server_24.repositories

import me.gistory.newbies_server_24.entities.Board
import me.gistory.newbies_server_24.entities.Post
import me.gistory.newbies_server_24.entities.Tag
import org.springframework.data.repository.CrudRepository
import java.util.UUID

interface PostRepository : CrudRepository<Post, UUID>
interface PostRepository : CrudRepository<Post, UUID> {
fun findByTags(tag: Tag): List<Post>
fun findByBoard(board: Board): List<Post>
fun findByTagsAndBoard(tag: Tag, board: Board): List<Post>
fun findByTitleContainingIgnoreCaseOrBodyContainingIgnoreCase(titleContainingIgnoreCase: String, bodyContainingIgnoreCase: String): List<Post>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package me.gistory.newbies_server_24.services

import jakarta.transaction.Transactional
import me.gistory.newbies_server_24.dto.CreatePostDto
import me.gistory.newbies_server_24.dto.UpdatePostDto
import me.gistory.newbies_server_24.entities.Post
import me.gistory.newbies_server_24.exceptions.*
import me.gistory.newbies_server_24.repositories.AuthRepository
import me.gistory.newbies_server_24.repositories.BoardRepository
import me.gistory.newbies_server_24.repositories.PostRepository
import me.gistory.newbies_server_24.repositories.TagRepository
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.util.UUID

@Service
@Transactional
class PostService (
private val postRepository: PostRepository,
private val tagRepository: TagRepository,
private val boardRepository: BoardRepository,
private val authRepository: AuthRepository,
) {
private val logger = LoggerFactory.getLogger(AuthService::class.java)

fun getPosts(boardId: UUID?, tagId: String?): List<Post> {
if (boardId == null && tagId == null) {
return postRepository.findAll().toList()
} else if (boardId == null && tagId != null) {
val tag = tagRepository.findByIdOrNull(tagId) ?: return postRepository.findAll().toList()
return postRepository.findByTags(tag)
} else if (tagId == null && boardId != null) {
val board = boardRepository.findByIdOrNull(boardId) ?: return postRepository.findAll().toList()
return postRepository.findByBoard(board)
} else if (tagId != null && boardId != null) {
val tag = tagRepository.findByIdOrNull(tagId) ?: return postRepository.findAll().toList()
val board = boardRepository.findByIdOrNull(boardId) ?: return postRepository.findAll().toList()
return postRepository.findByTagsAndBoard(tag, board)
}
throw ForbiddenException()
}

fun searchPost(keyword: String): List<Post> {
return postRepository.findByTitleContainingIgnoreCaseOrBodyContainingIgnoreCase(keyword, keyword)
}

fun createPost(dto: CreatePostDto, boardId: UUID, userEmail: String): Post {
val board = boardRepository.findByIdOrNull(boardId) ?: throw BoardNotFoundException()
val user = authRepository.findByEmail(userEmail) ?: throw UserNotFoundException()
val tags = try {
tagRepository.findAllById(dto.tags).toMutableSet()
} catch(e: Error) {
throw TagNotFoundException()
}
return postRepository.save(Post(
title = dto.title,
body = dto.body,
tags = tags,
createdBy = user,
board = board,
))
}

fun updatePost(dto: UpdatePostDto, postId: UUID, userEmail: String): Post {
val post = postRepository.findByIdOrNull(postId) ?: throw PostNotFoundException()
val user = authRepository.findByEmail(userEmail) ?: throw UserNotFoundException()
if (post.createdBy.id != user.id) {
throw ForbiddenException()
}
post.title = dto.title ?: post.title
post.body = dto.body ?: post.body
if (dto.tags != null) {
val tags = try {
tagRepository.findAllById(dto.tags).toMutableSet()
} catch(e: Error) {
throw TagNotFoundException()
}
post.tags = tags
}
return postRepository.save(post)
}

fun deletePost(postId: UUID, userEmail: String): Unit {
val user = authRepository.findByEmail(userEmail) ?: throw UserNotFoundException()
val post = postRepository.findByIdOrNull(postId)
?: throw PostNotFoundException()
if (post.createdBy.id != user.id) {
throw ForbiddenException()
}
postRepository.delete(post)
}
}

0 comments on commit 4c93a60

Please sign in to comment.