-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dc4798e
commit a38bc10
Showing
11 changed files
with
427 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import jakarta.persistence.Column | ||
import jakarta.persistence.Embedded | ||
import jakarta.persistence.Entity | ||
import jakarta.persistence.EnumType | ||
import jakarta.persistence.Enumerated | ||
import jakarta.persistence.FetchType | ||
import jakarta.persistence.GeneratedValue | ||
import jakarta.persistence.GenerationType | ||
import jakarta.persistence.Id | ||
import jakarta.persistence.JoinColumn | ||
import jakarta.persistence.Lob | ||
import jakarta.persistence.ManyToOne | ||
|
||
@Entity | ||
class Content( | ||
newsInformation: NewsInformation, | ||
language: Language, | ||
content: String, | ||
) { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
val sequence: Long? = null | ||
|
||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | ||
@JoinColumn(name = "news_id", nullable = false) | ||
var news: News? = null | ||
private set | ||
|
||
@Embedded | ||
var newsInformation: NewsInformation = newsInformation | ||
private set | ||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(name = "language", nullable = false) | ||
var language: Language = language | ||
private set | ||
|
||
@Lob | ||
@Column(name = "content", nullable = false) | ||
var content: String = content | ||
private set | ||
|
||
fun initialNews(news: News) { | ||
if (this.news != null) { | ||
throw IllegalArgumentException("이미 뉴스에 등록된 컨텐츠 입니다.") // TODO 명확한 예외 정의할 것 | ||
} | ||
this.news = news | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/kotlin/kr/galaxyhub/sc/news/domain/EnumSetLanguageConverter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import jakarta.persistence.AttributeConverter | ||
import jakarta.persistence.Converter | ||
import java.util.EnumSet | ||
|
||
@Converter | ||
class EnumSetLanguageConverter : AttributeConverter<EnumSet<Language>, String> { | ||
|
||
override fun convertToDatabaseColumn(attribute: EnumSet<Language>): String { | ||
return attribute.joinToString(separator = SEPARATOR) | ||
} | ||
|
||
override fun convertToEntityAttribute(dbData: String?): EnumSet<Language> { | ||
return if (dbData.isNullOrBlank()) { | ||
EnumSet.noneOf(Language::class.java) | ||
} else { | ||
dbData.split(SEPARATOR) | ||
.map { Language.valueOf(it) } | ||
.toCollection(EnumSet.noneOf(Language::class.java)) | ||
} | ||
} | ||
|
||
companion object { | ||
|
||
private const val SEPARATOR = "," | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
enum class Language { | ||
ENGLISH, | ||
KOREAN, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import jakarta.persistence.CascadeType | ||
import jakarta.persistence.Column | ||
import jakarta.persistence.Convert | ||
import jakarta.persistence.Embedded | ||
import jakarta.persistence.Entity | ||
import jakarta.persistence.EnumType | ||
import jakarta.persistence.Enumerated | ||
import jakarta.persistence.FetchType | ||
import jakarta.persistence.OneToMany | ||
import java.time.LocalDateTime | ||
import java.util.EnumSet | ||
import kr.galaxyhub.sc.common.domain.PrimaryKeyEntity | ||
|
||
@Entity | ||
class News( | ||
newsType: NewsType, | ||
originId: Long, | ||
publishedAt: LocalDateTime, | ||
) : PrimaryKeyEntity() { | ||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(name = "news_type", nullable = false) | ||
var newsType: NewsType = newsType | ||
private set | ||
|
||
@Column(name = "origin_id", nullable = false) | ||
var originId: Long = originId | ||
private set | ||
|
||
@Column(name = "published_at", nullable = false) | ||
var publishedAt: LocalDateTime = publishedAt | ||
private set | ||
|
||
@Embedded | ||
var newsInformation: NewsInformation = NewsInformation.EMPTY | ||
private set | ||
|
||
@Convert(converter = EnumSetLanguageConverter::class) | ||
@Column(name = "support_languages", nullable = false) | ||
private val mutableSupportLanguages: EnumSet<Language> = EnumSet.noneOf(Language::class.java) | ||
val supportLanguages: Set<Language> get() = mutableSupportLanguages.toHashSet() | ||
|
||
@OneToMany(fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], mappedBy = "news") | ||
private val contents: MutableList<Content> = mutableListOf() | ||
|
||
fun addContent(content: Content) { | ||
val language = content.language | ||
if (mutableSupportLanguages.contains(language)) { | ||
throw IllegalArgumentException("이미 해당 언어로 작성된 뉴스가 있습니다.") // TODO 명확한 예외 정의할 것 | ||
} | ||
if (mutableSupportLanguages.isEmpty()) { | ||
newsInformation = content.newsInformation | ||
} | ||
contents.add(content) | ||
content.initialNews(this) | ||
mutableSupportLanguages.add(language) | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/kotlin/kr/galaxyhub/sc/news/domain/NewsInformation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import jakarta.persistence.Column | ||
import jakarta.persistence.Embeddable | ||
|
||
@Embeddable | ||
data class NewsInformation( | ||
@Column(name = "title", nullable = true) | ||
val title: String?, | ||
|
||
@Column(name = "excerpt", nullable = true) | ||
val excerpt: String? | ||
) { | ||
companion object { | ||
|
||
val EMPTY = NewsInformation(null, null) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
enum class NewsType { | ||
PATCH_NOTE, | ||
NEWS, | ||
} |
46 changes: 46 additions & 0 deletions
46
src/test/kotlin/kr/galaxyhub/sc/news/domain/ContentTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import io.kotest.assertions.throwables.shouldThrow | ||
import io.kotest.core.spec.style.DescribeSpec | ||
import io.kotest.matchers.shouldBe | ||
import io.kotest.matchers.throwable.shouldHaveMessage | ||
import kr.galaxyhub.sc.news.fixture.ContentFixture | ||
import kr.galaxyhub.sc.news.fixture.NewsFixture | ||
|
||
class ContentTest : DescribeSpec({ | ||
|
||
describe("constructor") { | ||
context("컨텐츠가 생성되면") { | ||
val content = ContentFixture.of(Language.ENGLISH) | ||
|
||
it("뉴스는 null이다.") { | ||
content.news shouldBe null | ||
} | ||
} | ||
} | ||
|
||
describe("initialNews") { | ||
context("컨텐츠가 뉴스에 등록된 상태이면") { | ||
val content = ContentFixture.of(Language.ENGLISH) | ||
content.initialNews(NewsFixture.create()) | ||
|
||
it("IllegalArgumentException 예외를 던진다.") { | ||
val exception = shouldThrow<IllegalArgumentException> { | ||
content.initialNews(NewsFixture.create()) | ||
} | ||
exception shouldHaveMessage "이미 뉴스에 등록된 컨텐츠 입니다." | ||
} | ||
} | ||
|
||
context("컨텐츠가 뉴스에 등록된 상태가 아니면") { | ||
val content = ContentFixture.of(Language.ENGLISH) | ||
|
||
it("성공적으로 등록된다.") { | ||
val news = NewsFixture.create() | ||
content.initialNews(news) | ||
|
||
content.news shouldBe news | ||
} | ||
} | ||
} | ||
}) |
91 changes: 91 additions & 0 deletions
91
src/test/kotlin/kr/galaxyhub/sc/news/domain/EnumSetLanguageConverterTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import io.kotest.assertions.assertSoftly | ||
import io.kotest.core.spec.style.DescribeSpec | ||
import io.kotest.inspectors.forAll | ||
import io.kotest.matchers.collections.shouldContainExactly | ||
import io.kotest.matchers.collections.shouldHaveSize | ||
import io.kotest.matchers.shouldBe | ||
import io.kotest.matchers.string.shouldContain | ||
import java.util.EnumSet | ||
|
||
class EnumSetLanguageConverterTest : DescribeSpec({ | ||
|
||
val enumSetLanguageConverter = EnumSetLanguageConverter() | ||
|
||
describe("convertToDatabaseColumn") { | ||
context("요소가 한 개 이면") { | ||
val attribute = EnumSet.of(Language.ENGLISH) | ||
|
||
it(",가 없는 문자열을 반환한다.") { | ||
val expect = enumSetLanguageConverter.convertToDatabaseColumn(attribute) | ||
|
||
expect shouldBe "ENGLISH" | ||
} | ||
} | ||
|
||
context("요소가 여러 개 이면") { | ||
val attribute = EnumSet.of(Language.ENGLISH, Language.KOREAN) | ||
|
||
it(",가 있는 문자열을 반환한다.") { | ||
val expect = enumSetLanguageConverter.convertToDatabaseColumn(attribute) | ||
|
||
assertSoftly { | ||
expect shouldContain "," | ||
expect shouldContain "ENGLISH" | ||
expect shouldContain "KOREAN" | ||
} | ||
} | ||
} | ||
|
||
context("요소가 없으면") { | ||
val attribute = EnumSet.noneOf(Language::class.java) | ||
|
||
it("빈 문자열을 반환한다.") { | ||
val expect = enumSetLanguageConverter.convertToDatabaseColumn(attribute) | ||
|
||
expect shouldBe "" | ||
} | ||
} | ||
} | ||
|
||
describe("convertToEntityAttribute") { | ||
context(",가 없는 문자열이면") { | ||
val dbData = "ENGLISH" | ||
|
||
it("한 개의 요소를 반환한다.") { | ||
val expect = enumSetLanguageConverter.convertToEntityAttribute(dbData) | ||
|
||
assertSoftly { | ||
expect shouldHaveSize 1 | ||
expect shouldContainExactly setOf(Language.ENGLISH) | ||
} | ||
} | ||
} | ||
|
||
context(",가 있는 문자열이면") { | ||
val dbData = "ENGLISH,KOREAN" | ||
|
||
it("여러 개의 요소를 반환한다.") { | ||
val expect = enumSetLanguageConverter.convertToEntityAttribute(dbData) | ||
|
||
assertSoftly { | ||
expect shouldHaveSize 2 | ||
expect shouldContainExactly setOf(Language.ENGLISH, Language.KOREAN) | ||
} | ||
} | ||
} | ||
|
||
context("null 또는 빈 문자열이면") { | ||
val dbDate = listOf(null, "", " ", "\t", "\n") | ||
|
||
it("비어 있는 요소를 반환한다.") { | ||
dbDate.forAll { | ||
val expect = enumSetLanguageConverter.convertToEntityAttribute(it) | ||
|
||
expect shouldHaveSize 0 | ||
} | ||
} | ||
} | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package kr.galaxyhub.sc.news.domain | ||
|
||
import io.kotest.assertions.assertSoftly | ||
import io.kotest.assertions.throwables.shouldThrow | ||
import io.kotest.core.spec.style.DescribeSpec | ||
import io.kotest.matchers.collections.shouldContainExactly | ||
import io.kotest.matchers.collections.shouldHaveSize | ||
import io.kotest.matchers.shouldBe | ||
import io.kotest.matchers.shouldNotBe | ||
import io.kotest.matchers.throwable.shouldHaveMessage | ||
import kr.galaxyhub.sc.news.fixture.ContentFixture | ||
import kr.galaxyhub.sc.news.fixture.NewsFixture | ||
|
||
class NewsTest : DescribeSpec({ | ||
|
||
describe("constructor") { | ||
context("뉴스가 생성되면") { | ||
val news = NewsFixture.create() | ||
|
||
it("supportLanguages는 비어있다.") { | ||
news.supportLanguages shouldHaveSize 0 | ||
} | ||
|
||
it("newsInformation은 null이 아닌, Empty이다.") { | ||
assertSoftly { | ||
news.newsInformation shouldNotBe null | ||
news.newsInformation shouldBe NewsInformation.EMPTY | ||
} | ||
} | ||
} | ||
} | ||
|
||
describe("addContent") { | ||
context("뉴스에 컨텐츠가 추가된 상태에서") { | ||
val news = NewsFixture.create() | ||
news.addContent(ContentFixture.of(Language.ENGLISH)) | ||
|
||
context("중복된 언어의 컨텐츠가 추가되면") { | ||
it("IllegalArgumentException 예외를 던진다.") { | ||
val exception = shouldThrow<IllegalArgumentException> { | ||
news.addContent(ContentFixture.of(Language.ENGLISH)) | ||
} | ||
exception shouldHaveMessage "이미 해당 언어로 작성된 뉴스가 있습니다." | ||
} | ||
} | ||
|
||
context("중복되지 않은 언어의 컨텐츠가 추가되면") { | ||
val content = ContentFixture.of(Language.KOREAN) | ||
news.addContent(content) | ||
|
||
it("성공적으로 추가된다.") { | ||
news.supportLanguages shouldContainExactly setOf(Language.KOREAN, Language.ENGLISH) | ||
} | ||
|
||
it("newsInformation은 변경되지 않는다.") { | ||
news.newsInformation shouldNotBe content.newsInformation | ||
} | ||
} | ||
} | ||
|
||
context("뉴스에 컨텐츠가 없는 상태에서") { | ||
val news = NewsFixture.create() | ||
context("컨텐츠가 추가되면") { | ||
val content = ContentFixture.of(Language.ENGLISH) | ||
news.addContent(content) | ||
|
||
it("성공적으로 추가된다.") { | ||
news.supportLanguages shouldContainExactly setOf(Language.ENGLISH) | ||
} | ||
|
||
it("newsInformation이 설정된다.") { | ||
news.newsInformation shouldBe content.newsInformation | ||
} | ||
} | ||
} | ||
} | ||
}) |
Oops, something went wrong.