Skip to content

Commit

Permalink
Merge pull request #98 from grida-diary/main
Browse files Browse the repository at this point in the history
Docs
  • Loading branch information
wwan13 authored Aug 1, 2024
2 parents 0010068 + 144ec95 commit 13359a5
Show file tree
Hide file tree
Showing 19 changed files with 203 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,15 @@ class DiaryImageController(
val response = IdResponse(generatedDiaryImageId)
return ApiResponse.success(response)
}

@PostMapping("/{diaryId}/image/{diaryImageId}")
fun applyDiaryImage(
@RequestUserId userId: Long,
@PathVariable diaryId: Long,
@PathVariable diaryImageId: Long
): ApiResponse<IdResponse> {
diaryImageService.applyDiaryImage(diaryImageId, diaryId, userId)
val response = IdResponse(diaryId)
return ApiResponse.success(response)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package org.grida.docs.diaryimage

import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import io.mockk.just
import io.mockk.runs
import io.wwan13.api.document.snippets.NUMBER
import io.wwan13.api.document.snippets.isTypeOf
import io.wwan13.api.document.snippets.whichMeans
Expand Down Expand Up @@ -43,4 +45,27 @@ class DiaryImageApiDocsTest(
)
}
}

@Test
fun `일기 이미지 적용 API`() {
every { diaryImageService.applyDiaryImage(any(), any(), any()) } just runs

val api = api.post("/api/v1/diary/{diaryId}/image/{diaryImageId}", 1L, 1L) {
withBearerToken()
}

documentFor(api, "apply-diary-image") {
summary("일기 이미지 적용 API")
requestHeaders(
"Authorization" whichMeans "인증 토큰"
)
pathParameters(
"diaryId" whichMeans "적용하려는 일기의 ID",
"diaryImageId" whichMeans "적용하려는 일기 이미지의 ID",
)
responseFields(
"data.id" isTypeOf NUMBER whichMeans "적용된 일기 ID"
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.grida.domain.base

import org.grida.error.AccessFailed
import org.grida.error.GridaException
import org.springframework.stereotype.Component

@Component
class AccessManager {

fun ownerOnly(
target: Ownable,
accessorId: Long
) {
if (!target.isOwner(accessorId)) {
throw GridaException(AccessFailed)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.grida.domain.base

interface Ownable {

fun isOwner(accessorId: Long): Boolean
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.grida.domain.diary

import org.grida.domain.base.Ownable
import org.grida.domain.base.Timestamp
import java.time.LocalDate

Expand All @@ -10,8 +11,9 @@ data class Diary(
val targetDate: LocalDate,
val content: String,
val scope: DiaryScope
) {
fun isOwner(assessorId: Long): Boolean {
return userId == assessorId
) : Ownable {

override fun isOwner(accessorId: Long): Boolean {
return userId == accessorId
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.grida.domain.diary

import org.grida.domain.base.AccessManager
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class DiaryModifier(
private val diaryRepository: DiaryRepository,
private val diaryReader: DiaryReader,
private val diaryValidator: DiaryValidator
private val accessManager: AccessManager
) {

@Transactional
Expand All @@ -18,7 +19,7 @@ class DiaryModifier(
scope: DiaryScope
) {
val diary = diaryReader.read(diaryId)
diaryValidator.validateIsOwner(diary, userId)
accessManager.ownerOnly(diary, userId)

diaryRepository.updateContent(diaryId, content)
diaryRepository.updateScope(diaryId, scope)
Expand All @@ -31,7 +32,7 @@ class DiaryModifier(
scope: DiaryScope
) {
val diary = diaryReader.read(diaryId)
diaryValidator.validateIsOwner(diary, userId)
accessManager.ownerOnly(diary, userId)

diaryRepository.updateScope(diaryId, scope)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package org.grida.domain.diaryimage

import org.grida.domain.base.Ownable
import org.grida.domain.image.Image

data class DiaryImage(
val id: Long = 0,
val userId: Long,
val diaryId: Long,
val image: Image
)
) : Ownable {

override fun isOwner(accessorId: Long): Boolean {
return userId == accessorId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.grida.domain.diaryimage

import org.grida.domain.base.AccessManager
import org.grida.domain.image.ImageStatus
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class DiaryImageModifier(
private val diaryImageRepository: DiaryImageRepository,
private val diaryImageReader: DiaryImageReader,
private val accessManager: AccessManager
) {

@Transactional
fun modifyAsActivate(diaryImageId: Long, userId: Long) {
val diaryImage = diaryImageReader.read(diaryImageId)
accessManager.ownerOnly(diaryImage, userId)

diaryImageRepository.updateStatus(diaryImageId, ImageStatus.ACTIVATE)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.grida.domain.diaryimage

import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class DiaryImageReader(
private val diaryImageRepository: DiaryImageRepository
) {

@Transactional(readOnly = true)
fun read(diaryId: Long): DiaryImage {
return diaryImageRepository.findById(diaryId)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package org.grida.domain.diaryimage

import org.grida.domain.diary.Diary
import org.grida.domain.image.ImageStatus
import org.grida.domain.user.User

interface DiaryImageRepository {
fun save(diaryImage: DiaryImage, diary: Diary, user: User): Long

fun findById(id: Long): DiaryImage

fun existsByDiaryIdAndStatus(diaryId: Long, status: ImageStatus): Boolean

fun updateStatus(diaryImageId: Long, status: ImageStatus): Long
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.grida.domain.diaryimage

import org.grida.domain.base.AccessManager
import org.grida.domain.diary.DiaryReader
import org.grida.domain.diary.DiaryValidator
import org.grida.domain.image.Image
import org.grida.domain.image.ImageStatus
import org.springframework.stereotype.Service
Expand All @@ -10,16 +10,18 @@ import org.springframework.stereotype.Service
class DiaryImageService(
private val diaryImageAppender: DiaryImageAppender,
private val diaryImageGenerator: DiaryImageGenerator,
private val diaryImageModifier: DiaryImageModifier,
private val diaryImageValidator: DiaryImageValidator,
private val diaryReader: DiaryReader,
private val diaryValidator: DiaryValidator
private val accessManager: AccessManager
) {

fun generateDiaryImage(
diaryId: Long,
userId: Long
): Long {
val diary = diaryReader.read(diaryId)
diaryValidator.validateIsOwner(diary, userId)
accessManager.ownerOnly(diary, userId)
val generatedImageUrl = diaryImageGenerator.generate(diary.content)

val diaryImage = DiaryImage(
Expand All @@ -29,4 +31,13 @@ class DiaryImageService(
)
return diaryImageAppender.append(diaryImage, diaryId, userId)
}

fun applyDiaryImage(
diaryImageId: Long,
diaryId: Long,
userId: Long
) {
diaryImageValidator.validateAlreadyHasActivateDiaryImage(diaryId)
diaryImageModifier.modifyAsActivate(diaryImageId, userId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.grida.domain.diaryimage

import org.grida.domain.image.ImageStatus
import org.grida.error.ActivateImageAlreadyExists
import org.grida.error.GridaException
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
class DiaryImageValidator(
private val diaryImageRepository: DiaryImageRepository
) {

@Transactional(readOnly = true)
fun validateAlreadyHasActivateDiaryImage(diaryId: Long) {
if (diaryImageRepository.existsByDiaryIdAndStatus(diaryId, ImageStatus.ACTIVATE)) {
throw GridaException(ActivateImageAlreadyExists)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.grida.domain.profileimage

import org.grida.domain.base.Ownable
import org.grida.domain.image.Image

data class ProfileImage(
val id: Long = 0,
val userId: Long,
val image: Image,
val appearance: Appearance
) {
fun isOwner(accessorId: Long): Boolean {
) : Ownable {

override fun isOwner(accessorId: Long): Boolean {
return userId == accessorId
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.grida.domain.profileimage

import org.grida.domain.base.AccessManager
import org.grida.domain.image.ImageStatus
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
Expand All @@ -8,7 +9,7 @@ import org.springframework.transaction.annotation.Transactional
class ProfileImageModifier(
private val profileImageRepository: ProfileImageRepository,
private val profileImageReader: ProfileImageReader,
private val profileImageValidator: ProfileImageValidator
private val accessManager: AccessManager
) {

@Transactional
Expand All @@ -17,7 +18,7 @@ class ProfileImageModifier(
profileImageId: Long
) {
val profileImage = profileImageReader.read(profileImageId)
profileImageValidator.validateIsOwner(profileImage, userId)
accessManager.ownerOnly(profileImage, userId)

profileImageRepository.updateStatue(profileImageId, ImageStatus.ACTIVATE)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.grida.domain.profileimage

import org.grida.domain.image.ImageStatus
import org.grida.error.AccessFailed
import org.grida.error.ActivateProfileImageAlreadyExists
import org.grida.error.ActivateImageAlreadyExists
import org.grida.error.GridaException
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
Expand All @@ -12,19 +11,10 @@ class ProfileImageValidator(
private val profileImageRepository: ProfileImageRepository
) {

fun validateIsOwner(
profileImage: ProfileImage,
userId: Long
) {
if (!profileImage.isOwner(userId)) {
throw GridaException(AccessFailed)
}
}

@Transactional(readOnly = true)
fun validateAlreadyHasActivateProfileImage(userId: Long) {
if (profileImageRepository.existsByUserIdAndStatus(userId, ImageStatus.ACTIVATE)) {
throw GridaException(ActivateProfileImageAlreadyExists)
throw GridaException(ActivateImageAlreadyExists)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ data object UnusableUsername : CoreDomainErrorType {
override val logLevel: LogLevel = INFO
}

data object ActivateProfileImageAlreadyExists : CoreDomainErrorType {
data object ActivateImageAlreadyExists : CoreDomainErrorType {
override val httpStatusCode: Int = BAD_REQUEST
override val errorCode: String = "PROFILE_IMAGE_400_1"
override val message: String = "이미 활성화된 프로필 이미지가 존재합니다."
override val errorCode: String = "IMAGE_400_1"
override val message: String = "이미 활성화된 이미지가 존재합니다."
override val logLevel: LogLevel = INFO
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ class DiaryImageEntity(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "diary_id")
var diary: DiaryEntity,
) : BaseEntity()
) : BaseEntity() {

fun updateStatue(status: ImageStatus) {
this.status = status
}
}
Loading

0 comments on commit 13359a5

Please sign in to comment.