diff --git a/app-server/subprojects/bounded_context/user/application/build.gradle.kts b/app-server/subprojects/bounded_context/user/application/build.gradle.kts index e69de29bb..4b3dd88f1 100644 --- a/app-server/subprojects/bounded_context/user/application/build.gradle.kts +++ b/app-server/subprojects/bounded_context/user/application/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + implementation(projects.crossCuttingConcern.application.serverEvent) +} diff --git a/app-server/subprojects/bounded_context/user/application/src/main/kotlin/club/staircrusher/user/application/port/in/UserApplicationService.kt b/app-server/subprojects/bounded_context/user/application/src/main/kotlin/club/staircrusher/user/application/port/in/UserApplicationService.kt index 13c281232..8a70b0af0 100644 --- a/app-server/subprojects/bounded_context/user/application/src/main/kotlin/club/staircrusher/user/application/port/in/UserApplicationService.kt +++ b/app-server/subprojects/bounded_context/user/application/src/main/kotlin/club/staircrusher/user/application/port/in/UserApplicationService.kt @@ -1,5 +1,7 @@ package club.staircrusher.user.application.port.`in` +import club.staircrusher.application.server_event.port.`in`.SccServerEventRecorder +import club.staircrusher.domain.server_event.NewsletterSubscribedOnSignupPayload import club.staircrusher.stdlib.clock.SccClock import club.staircrusher.stdlib.di.annotation.Component import club.staircrusher.stdlib.domain.SccDomainException @@ -25,6 +27,7 @@ class UserApplicationService( private val passwordEncryptor: PasswordEncryptor, private val userAuthInfoRepository: UserAuthInfoRepository, private val stibeeSubscriptionService: StibeeSubscriptionService, + private val sccServerEventRecorder: SccServerEventRecorder, ) { @Deprecated("닉네임 로그인은 사라질 예정") @@ -133,7 +136,7 @@ class UserApplicationService( if (isNewsLetterSubscriptionAgreed) { transactionManager.doAfterCommit { - user.email?.let { subscribeToNewsLetter(it, user.nickname) } + user.email?.let { subscribeToNewsLetter(user.id, it, user.nickname) } } } @@ -162,7 +165,8 @@ class UserApplicationService( return userRepository.findAll() } - private fun subscribeToNewsLetter(email: String, name: String) { + private fun subscribeToNewsLetter(userId: String, email: String, name: String) { + sccServerEventRecorder.record(NewsletterSubscribedOnSignupPayload(userId)) runBlocking { stibeeSubscriptionService.registerSubscriber( email = email, diff --git a/app-server/subprojects/bounded_context/user/infra/build.gradle.kts b/app-server/subprojects/bounded_context/user/infra/build.gradle.kts index c9606121d..a23e46018 100644 --- a/app-server/subprojects/bounded_context/user/infra/build.gradle.kts +++ b/app-server/subprojects/bounded_context/user/infra/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { api(projects.apiSpecification.api) implementation(projects.crossCuttingConcern.infra.persistenceModel) + implementation(projects.crossCuttingConcern.infra.serverEvent) implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework:spring-webflux") implementation("io.projectreactor.netty:reactor-netty") diff --git a/app-server/subprojects/bounded_context/user/infra/src/integrationTest/kotlin/club/staircrusher/user/infra/adapter/in/controller/UpdateUserInfoTest.kt b/app-server/subprojects/bounded_context/user/infra/src/integrationTest/kotlin/club/staircrusher/user/infra/adapter/in/controller/UpdateUserInfoTest.kt index e20061a9c..1f5f0a0da 100644 --- a/app-server/subprojects/bounded_context/user/infra/src/integrationTest/kotlin/club/staircrusher/user/infra/adapter/in/controller/UpdateUserInfoTest.kt +++ b/app-server/subprojects/bounded_context/user/infra/src/integrationTest/kotlin/club/staircrusher/user/infra/adapter/in/controller/UpdateUserInfoTest.kt @@ -3,6 +3,8 @@ package club.staircrusher.user.infra.adapter.`in`.controller import club.staircrusher.api.spec.dto.ApiErrorResponse import club.staircrusher.api.spec.dto.UpdateUserInfoPost200Response import club.staircrusher.api.spec.dto.UpdateUserInfoPostRequest +import club.staircrusher.application.server_event.port.`in`.SccServerEventRecorder +import club.staircrusher.domain.server_event.NewsletterSubscribedOnSignupPayload import club.staircrusher.stdlib.testing.SccRandom import club.staircrusher.user.application.port.out.web.subscription.StibeeSubscriptionService import club.staircrusher.user.domain.model.UserMobilityTool @@ -16,6 +18,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.eq import org.mockito.kotlin.never +import org.mockito.kotlin.verify import org.mockito.kotlin.verifyBlocking import org.springframework.boot.test.mock.mockito.MockBean @@ -23,6 +26,9 @@ class UpdateUserInfoTest : UserITBase() { @MockBean lateinit var stibeeSubscriptionService: StibeeSubscriptionService + @MockBean + lateinit var sccServerEventRecorder: SccServerEventRecorder + @Test fun updateUserInfoTest() { val user = transactionManager.doInTransaction { @@ -196,6 +202,7 @@ class UpdateUserInfoTest : UserITBase() { } .apply { verifyBlocking(stibeeSubscriptionService, atLeastOnce()) { registerSubscriber(eq(changedEmail), eq(changedNickname), any()) } + verify(sccServerEventRecorder, atLeastOnce()).record(NewsletterSubscribedOnSignupPayload(user.id)) } } @@ -230,6 +237,7 @@ class UpdateUserInfoTest : UserITBase() { } .apply { verifyBlocking(stibeeSubscriptionService, never()) { registerSubscriber(eq(changedEmail), eq(changedNickname), any()) } + verify(sccServerEventRecorder, never()).record(NewsletterSubscribedOnSignupPayload(user.id)) } } @@ -265,6 +273,7 @@ class UpdateUserInfoTest : UserITBase() { } .apply { verifyBlocking(stibeeSubscriptionService, never()) { registerSubscriber(eq(changedEmail), eq(changedNickname), any()) } + verify(sccServerEventRecorder, never()).record(NewsletterSubscribedOnSignupPayload(user.id)) } } } diff --git a/app-server/subprojects/cross_cutting_concern/application/server_event/build.gradle.kts b/app-server/subprojects/cross_cutting_concern/application/server_event/build.gradle.kts new file mode 100644 index 000000000..1dc4ab346 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/application/server_event/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + implementation(rootProject.projects.crossCuttingConcern.stdlib) + api(rootProject.projects.crossCuttingConcern.domain.serverEvent) +} diff --git a/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerEventRecorder.kt b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerEventRecorder.kt new file mode 100644 index 000000000..b8f4fb562 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerEventRecorder.kt @@ -0,0 +1,7 @@ +package club.staircrusher.application.server_event.port.`in` + +import club.staircrusher.domain.server_event.ServerEventPayload + +interface SccServerEventRecorder { + fun record(payload: ServerEventPayload) +} diff --git a/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerPersistentEventRecorder.kt b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerPersistentEventRecorder.kt new file mode 100644 index 000000000..290786835 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/in/SccServerPersistentEventRecorder.kt @@ -0,0 +1,35 @@ +package club.staircrusher.application.server_event.port.`in` + +import club.staircrusher.application.server_event.port.out.persistence.ServerEventRepository +import club.staircrusher.domain.server_event.ServerEvent +import club.staircrusher.domain.server_event.ServerEventPayload +import club.staircrusher.stdlib.clock.SccClock +import club.staircrusher.stdlib.di.annotation.Component +import club.staircrusher.stdlib.domain.entity.EntityIdGenerator +import club.staircrusher.stdlib.persistence.TransactionManager +import mu.KotlinLogging + +@Component +class SccServerPersistentEventRecorder( + private val transactionManager: TransactionManager, + private val serverEventRepository: ServerEventRepository, +) : SccServerEventRecorder { + private val logger = KotlinLogging.logger { } + + override fun record(payload: ServerEventPayload) = transactionManager.doInTransaction { + val serverEvent = try { + ServerEvent( + id = EntityIdGenerator.generateRandom(), + type = payload.type, + payload = payload, + createdAt = SccClock.instant(), + ) + } catch (e: Exception){ + logger.error(e) { "Failed to create server event of payload: $payload" } + null + } + + serverEvent?.let { serverEventRepository.save(it) } + Unit + } +} diff --git a/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/out/persistence/ServerEventRepository.kt b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/out/persistence/ServerEventRepository.kt new file mode 100644 index 000000000..10f11b5b3 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/application/server_event/src/main/kotlin/club/staircrusher/application/server_event/port/out/persistence/ServerEventRepository.kt @@ -0,0 +1,7 @@ +package club.staircrusher.application.server_event.port.out.persistence + +import club.staircrusher.domain.server_event.ServerEvent + +interface ServerEventRepository { + fun save(entity: ServerEvent): ServerEvent +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/build.gradle.kts b/app-server/subprojects/cross_cutting_concern/domain/server_event/build.gradle.kts new file mode 100644 index 000000000..19aa57066 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/build.gradle.kts @@ -0,0 +1,8 @@ +dependencies { + val kotlinxSerializationVersion: String by project + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") + + val jUnitJupiterVersion: String by project + testImplementation("org.junit.jupiter:junit-jupiter-api:$jUnitJupiterVersion") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jUnitJupiterVersion") +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/NewsletterSubscribedOnSignupPayload.kt b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/NewsletterSubscribedOnSignupPayload.kt new file mode 100644 index 000000000..df1cc7686 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/NewsletterSubscribedOnSignupPayload.kt @@ -0,0 +1,7 @@ +package club.staircrusher.domain.server_event + +data class NewsletterSubscribedOnSignupPayload( + val userId: String, +) : ServerEventPayload { + override val type = ServerEventType.NEWSLETTER_SUBSCRIBED_ON_SIGN_UP +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEvent.kt b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEvent.kt new file mode 100644 index 000000000..027a02ded --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEvent.kt @@ -0,0 +1,14 @@ +package club.staircrusher.domain.server_event + +import java.time.Instant + +data class ServerEvent( + val id: String, + val type: ServerEventType, + val payload: ServerEventPayload, + val createdAt: Instant, +) { + init { + check(type == payload.type) + } +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventPayload.kt b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventPayload.kt new file mode 100644 index 000000000..0464fba4c --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventPayload.kt @@ -0,0 +1,5 @@ +package club.staircrusher.domain.server_event + +interface ServerEventPayload { + val type: ServerEventType +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventType.kt b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventType.kt new file mode 100644 index 000000000..ec9089391 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/main/kotlin/club/staircrusher/domain/server_event/ServerEventType.kt @@ -0,0 +1,8 @@ +package club.staircrusher.domain.server_event + +enum class ServerEventType { + NEWSLETTER_SUBSCRIBED_ON_SIGN_UP, + // For test + // TODO: event type 이 더 생기면 테스트 코드 변경하고 삭제하기 + UNKNOWN, +} diff --git a/app-server/subprojects/cross_cutting_concern/domain/server_event/src/unitTest/kotlin/club/staircrusher/domain/server_event/ServerEventTest.kt b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/unitTest/kotlin/club/staircrusher/domain/server_event/ServerEventTest.kt new file mode 100644 index 000000000..bbf5a87ff --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/domain/server_event/src/unitTest/kotlin/club/staircrusher/domain/server_event/ServerEventTest.kt @@ -0,0 +1,32 @@ +package club.staircrusher.domain.server_event + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows +import java.time.Instant + +class ServerEventTest { + + @Test + fun `ServerEvent 의 type 과 주어진 payload 의 type 이 일치해야 한다`() { + val serverEventPayload = NewsletterSubscribedOnSignupPayload("example") + + assertDoesNotThrow { + ServerEvent( + id = "example", + type = ServerEventType.NEWSLETTER_SUBSCRIBED_ON_SIGN_UP, + payload = serverEventPayload, + createdAt = Instant.now(), + ) + } + + assertThrows { + ServerEvent( + id = "example", + type = ServerEventType.UNKNOWN, + payload = serverEventPayload, + createdAt = Instant.now(), + ) + } + } +} diff --git a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/build.gradle.kts b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/build.gradle.kts index fe720ca68..26dce4cc2 100644 --- a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/build.gradle.kts +++ b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(projects.boundedContext.challenge.domain) implementation(projects.boundedContext.quest.domain) implementation(projects.boundedContext.user.domain) + implementation(projects.crossCuttingConcern.domain.serverEvent) val sqlDelightVersion: String by project api("app.cash.sqldelight:runtime-jvm:$sqlDelightVersion") diff --git a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/kotlin/club/staircrusher/infra/persistence/sqldelight/DB.kt b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/kotlin/club/staircrusher/infra/persistence/sqldelight/DB.kt index 592cd0ebe..83d3bafe2 100644 --- a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/kotlin/club/staircrusher/infra/persistence/sqldelight/DB.kt +++ b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/kotlin/club/staircrusher/infra/persistence/sqldelight/DB.kt @@ -1,6 +1,9 @@ package club.staircrusher.infra.persistence.sqldelight +import app.cash.sqldelight.ColumnAdapter import club.staircrusher.challenge.domain.model.ChallengeCondition +import club.staircrusher.domain.server_event.ServerEventPayload +import club.staircrusher.domain.server_event.ServerEventType import club.staircrusher.infra.persistence.sqldelight.column_adapter.AccessibilityImageListStringColumnAdapter import club.staircrusher.infra.persistence.sqldelight.column_adapter.ClubQuestPurposeTypeStringColumnAdapter import club.staircrusher.infra.persistence.sqldelight.column_adapter.EntranceDoorTypeListStringColumnAdapter @@ -19,6 +22,7 @@ import club.staircrusher.infra.persistence.sqldelight.migration.Club_quest import club.staircrusher.infra.persistence.sqldelight.migration.Place import club.staircrusher.infra.persistence.sqldelight.migration.Place_accessibility import club.staircrusher.infra.persistence.sqldelight.migration.Scc_user +import club.staircrusher.infra.persistence.sqldelight.migration.Server_event import club.staircrusher.infra.persistence.sqldelight.migration.User_auth_info import club.staircrusher.stdlib.di.annotation.Component import club.staircrusher.stdlib.persistence.Transaction @@ -79,6 +83,26 @@ class DB(dataSource: DataSource) : TransactionManager { ), club_questAdapter = Club_quest.Adapter( purpose_typeAdapter = ClubQuestPurposeTypeStringColumnAdapter, + ), + server_eventAdapter = Server_event.Adapter( + typeAdapter = object : ColumnAdapter { + override fun decode(databaseValue: String): ServerEventType { + return ServerEventType.valueOf(databaseValue) + } + + override fun encode(value: ServerEventType): String { + return value.name + } + }, + payloadAdapter = object : ColumnAdapter { + override fun decode(databaseValue: String): ServerEventPayload { + return objectMapper.readValue(databaseValue) + } + + override fun encode(value: ServerEventPayload): String { + return objectMapper.writeValueAsString(value) + } + } ) ) @@ -102,6 +126,7 @@ class DB(dataSource: DataSource) : TransactionManager { val challengeContributionQueries = scc.challengeContributionQueries val challengeParticipationQueries = scc.challengeParticipationQueries val challengeRankQueries = scc.challengeRankQueries + val serverEventQueries = scc.serverEventQueries override fun doInTransaction(block: Transaction.() -> T): T { diff --git a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/migration/V26__add_server_event.sqm b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/migration/V26__add_server_event.sqm new file mode 100644 index 000000000..b334a6312 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/migration/V26__add_server_event.sqm @@ -0,0 +1,13 @@ +import club.staircrusher.domain.server_event.ServerEventPayload; +import club.staircrusher.domain.server_event.ServerEventType; + + +CREATE TABLE IF NOT EXISTS server_event ( + id VARCHAR(36) NOT NULL, + type VARCHAR(64) AS ServerEventType NOT NULL, + payload TEXT AS ServerEventPayload NOT NULL DEFAULT '', + created_at TIMESTAMP(6) WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +CREATE INDEX idx_server_event_1 ON server_event(type, created_at); diff --git a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/query/server_event/ServerEvent.sq b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/query/server_event/ServerEvent.sq new file mode 100644 index 000000000..93805d5f4 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/sqldelight/club/staircrusher/infra/persistence/sqldelight/query/server_event/ServerEvent.sq @@ -0,0 +1,8 @@ +save: +INSERT INTO server_event +VALUES :server_event +ON CONFLICT(id) DO UPDATE SET + id = EXCLUDED.id, + type = EXCLUDED.type, + payload = EXCLUDED.payload, + created_at = EXCLUDED.created_at; diff --git a/app-server/subprojects/cross_cutting_concern/infra/server_event/build.gradle.kts b/app-server/subprojects/cross_cutting_concern/infra/server_event/build.gradle.kts new file mode 100644 index 000000000..d22bfec30 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/server_event/build.gradle.kts @@ -0,0 +1,6 @@ +dependencies { + implementation(projects.crossCuttingConcern.stdlib) + implementation(projects.crossCuttingConcern.infra.persistenceModel) + api(rootProject.projects.crossCuttingConcern.domain.serverEvent) + api(rootProject.projects.crossCuttingConcern.application.serverEvent) +} diff --git a/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/ServerEventRepository.kt b/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/ServerEventRepository.kt new file mode 100644 index 000000000..0d946f5f7 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/ServerEventRepository.kt @@ -0,0 +1,19 @@ +package club.staircrusher.infra.server_event.out.persistence + +import club.staircrusher.application.server_event.port.out.persistence.ServerEventRepository +import club.staircrusher.domain.server_event.ServerEvent +import club.staircrusher.infra.persistence.sqldelight.DB +import club.staircrusher.infra.server_event.out.persistence.sqldelight.toPersistenceModel +import club.staircrusher.stdlib.di.annotation.Component + +@Component +class ServerEventRepository( + db: DB, +) : ServerEventRepository { + private val queries = db.serverEventQueries + + override fun save(entity: ServerEvent): ServerEvent { + queries.save(entity.toPersistenceModel()) + return entity + } +} diff --git a/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/sqldelight/Converters.kt b/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/sqldelight/Converters.kt new file mode 100644 index 000000000..caee34184 --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/server_event/src/main/kotlin/club/staircrusher/infra/server_event/out/persistence/sqldelight/Converters.kt @@ -0,0 +1,19 @@ +package club.staircrusher.infra.server_event.out.persistence.sqldelight + +import club.staircrusher.domain.server_event.ServerEvent +import club.staircrusher.infra.persistence.sqldelight.migration.Server_event +import club.staircrusher.stdlib.time.toOffsetDateTime + +fun ServerEvent.toPersistenceModel() = Server_event( + id = id, + type = type, + payload = payload, + created_at = createdAt.toOffsetDateTime(), +) + +fun Server_event.toDomainModel() = ServerEvent( + id = id, + type = type, + payload = payload, + createdAt = created_at.toInstant(), +) diff --git a/app-server/subprojects/cross_cutting_concern/infra/spring_web/build.gradle.kts b/app-server/subprojects/cross_cutting_concern/infra/spring_web/build.gradle.kts index be8905d14..fce420442 100644 --- a/app-server/subprojects/cross_cutting_concern/infra/spring_web/build.gradle.kts +++ b/app-server/subprojects/cross_cutting_concern/infra/spring_web/build.gradle.kts @@ -18,4 +18,5 @@ dependencies { implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonModuleKotlinVersion") integrationTestImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonModuleKotlinVersion") integrationTestImplementation("org.springframework.boot:spring-boot-starter-test") + integrationTestImplementation(projects.crossCuttingConcern.application.serverEvent) } diff --git a/app-server/subprojects/cross_cutting_concern/infra/spring_web/src/integrationTest/kotlin/club/staircrusher/spring_web/mock/MockServerEventRepository.kt b/app-server/subprojects/cross_cutting_concern/infra/spring_web/src/integrationTest/kotlin/club/staircrusher/spring_web/mock/MockServerEventRepository.kt new file mode 100644 index 000000000..5815683df --- /dev/null +++ b/app-server/subprojects/cross_cutting_concern/infra/spring_web/src/integrationTest/kotlin/club/staircrusher/spring_web/mock/MockServerEventRepository.kt @@ -0,0 +1,17 @@ +package club.staircrusher.spring_web.mock + +import club.staircrusher.application.server_event.port.out.persistence.ServerEventRepository +import club.staircrusher.domain.server_event.ServerEvent +import club.staircrusher.stdlib.di.annotation.Component + +@Component +class MockServerEventRepository : ServerEventRepository { + override fun save(entity: ServerEvent): ServerEvent { + return ServerEvent( + id = entity.id, + type = entity.type, + payload = entity.payload, + createdAt = entity.createdAt + ) + } +}