diff --git a/build.gradle.kts b/build.gradle.kts index bd15bb2..f9511e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { testImplementation("org.springframework.cloud:spring-cloud-starter-contract-stub-runner") testImplementation("org.springframework.security:spring-security-test") testImplementation("org.awaitility:awaitility-kotlin:4.2.0") + testImplementation("com.ninja-squad:springmockk:4.0.2") } dependencyManagement { diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProvider.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProvider.kt new file mode 100644 index 0000000..5df6f92 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProvider.kt @@ -0,0 +1,41 @@ +package net.leanix.vsm.gitlab.broker.logs.adapter.feign + +import feign.FeignException +import net.leanix.vsm.githubbroker.logs.adapter.feign.data.AdminRequest +import net.leanix.vsm.githubbroker.logs.adapter.feign.data.StatusRequest +import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog +import net.leanix.vsm.gitlab.broker.logs.domain.LogProvider +import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component + +private const val FAILED_TO_SEND_STATUS_LOG = "Failed to send Status Log" +private const val FAILED_TO_SEND_ADMIN_LOG = "Failed to send Admin Log" + +@Component +class FeignLogProvider( + private val loggingClient: LoggingClient, + private val statusLogClient: StatusLogClient +) : LogProvider { + + private val logger: Logger = LoggerFactory.getLogger(FeignLogProvider::class.java) + + override fun sendAdminLog(adminLog: AdminLog) { + try { + loggingClient.sendAdminLog(AdminRequest.fromDomain(adminLog)) + } catch (e: FeignException) { + val message = "$FAILED_TO_SEND_ADMIN_LOG, ${e.message}" + logger.error(message) + } + } + + override fun sendStatusLog(statusLog: StatusLog) { + try { + statusLogClient.sendStatusLog(StatusRequest.fromDomain(statusLog)) + } catch (e: FeignException) { + val message = "$FAILED_TO_SEND_STATUS_LOG, ${e.message}" + logger.error(message) + } + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/LoggingClient.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/LoggingClient.kt new file mode 100644 index 0000000..3cf38b7 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/LoggingClient.kt @@ -0,0 +1,19 @@ +package net.leanix.vsm.gitlab.broker.logs.adapter.feign + +import net.leanix.vsm.githubbroker.logs.adapter.feign.data.AdminRequest +import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.config.MtmFeignClientConfiguration +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody + +@FeignClient( + name = "loggingClient", + url = "\${leanix.vsm.event-broker.base-url}", + configuration = [MtmFeignClientConfiguration::class] +) +interface LoggingClient { + + @PostMapping(value = ["/logs/admin"], consumes = [MediaType.APPLICATION_JSON_VALUE]) + fun sendAdminLog(@RequestBody request: AdminRequest) +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/StatusLogClient.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/StatusLogClient.kt new file mode 100644 index 0000000..e9a7a04 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/StatusLogClient.kt @@ -0,0 +1,19 @@ +package net.leanix.vsm.gitlab.broker.logs.adapter.feign + +import net.leanix.vsm.githubbroker.logs.adapter.feign.data.StatusRequest +import net.leanix.vsm.gitlab.broker.shared.auth.adapter.feign.config.MtmFeignClientConfiguration +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody + +@FeignClient( + name = "statusLoggingClient", + url = "\${leanix.vsm.base-url}", + configuration = [MtmFeignClientConfiguration::class] +) +interface StatusLogClient { + + @PostMapping(value = ["/log/connector-status"], consumes = [MediaType.APPLICATION_JSON_VALUE]) + fun sendStatusLog(@RequestBody request: StatusRequest) +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/AdminRequest.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/AdminRequest.kt new file mode 100644 index 0000000..27d4e69 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/AdminRequest.kt @@ -0,0 +1,31 @@ +package net.leanix.vsm.githubbroker.logs.adapter.feign.data + +import jakarta.validation.constraints.NotNull +import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog +import net.leanix.vsm.gitlab.broker.logs.domain.LogLevel +import java.util.UUID + +data class AdminRequest( + @field:NotNull(message = "Field \"runId\" cannot be empty") + val runId: UUID?, + @field:NotNull(message = "Field \"configurationId\" cannot be empty") + val configurationId: UUID?, + @field:NotNull(message = "Field \"subject\" cannot be empty") + val subject: String?, + @field:NotNull(message = "Field \"level\" cannot be empty") + val level: LogLevel?, + @field:NotNull(message = "Field \"message\" cannot be empty") + val message: String? +) { + companion object { + fun fromDomain(admin: AdminLog): AdminRequest { + return AdminRequest( + runId = admin.runId, + configurationId = admin.configurationId, + subject = admin.subject, + level = admin.level, + message = admin.message, + ) + } + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/StatusRequest.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/StatusRequest.kt new file mode 100644 index 0000000..36e6d35 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/data/StatusRequest.kt @@ -0,0 +1,24 @@ +package net.leanix.vsm.githubbroker.logs.adapter.feign.data + +import jakarta.validation.constraints.NotNull +import net.leanix.vsm.gitlab.broker.logs.domain.LogStatus +import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog +import java.util.UUID + +data class StatusRequest( + @field:NotNull(message = "Field \"runId\" cannot be empty") + val runId: UUID?, + @field:NotNull(message = "Field \"status\" cannot be empty") + val status: LogStatus?, + val message: String? = null +) { + companion object { + fun fromDomain(status: StatusLog): StatusRequest { + return StatusRequest( + runId = status.runId, + status = status.status, + message = status.message + ) + } + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingService.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingService.kt new file mode 100644 index 0000000..0a181fa --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingService.kt @@ -0,0 +1,25 @@ +package net.leanix.vsm.gitlab.broker.logs.application + +import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog +import net.leanix.vsm.gitlab.broker.logs.domain.LogProvider +import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class LoggingService( + private val logProvider: LogProvider +) { + private val logger: Logger = LoggerFactory.getLogger(LoggingService::class.java) + + fun sendStatusLog(statusLog: StatusLog) { + logger.info("Sending status log with runId: ${statusLog.runId}") + logProvider.sendStatusLog(statusLog) + } + + fun sendAdminLog(adminLog: AdminLog) { + logger.info("Sending admin log with runId: ${adminLog.runId}") + logProvider.sendAdminLog(adminLog) + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/AdminLog.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/AdminLog.kt new file mode 100644 index 0000000..4b2f95d --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/AdminLog.kt @@ -0,0 +1,11 @@ +package net.leanix.vsm.gitlab.broker.logs.domain + +import java.util.UUID + +data class AdminLog( + val runId: UUID, + val configurationId: UUID, + val subject: String, + val level: LogLevel, + val message: String? +) diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogLevel.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogLevel.kt new file mode 100644 index 0000000..609efe2 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogLevel.kt @@ -0,0 +1,7 @@ +package net.leanix.vsm.gitlab.broker.logs.domain + +enum class LogLevel { + INFO, + WARNING, + ERROR +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogProvider.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogProvider.kt new file mode 100644 index 0000000..a8dbf4c --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogProvider.kt @@ -0,0 +1,7 @@ +package net.leanix.vsm.gitlab.broker.logs.domain + + +interface LogProvider { + fun sendAdminLog(adminLog: AdminLog) + fun sendStatusLog(statusLog: StatusLog) +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogStatus.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogStatus.kt new file mode 100644 index 0000000..576c1f8 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/LogStatus.kt @@ -0,0 +1,8 @@ +package net.leanix.vsm.gitlab.broker.logs.domain + +enum class LogStatus { + STARTED, + IN_PROGRESS, + FAILED, + SUCCESSFUL +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/StatusLog.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/StatusLog.kt new file mode 100644 index 0000000..bd2e63c --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/logs/domain/StatusLog.kt @@ -0,0 +1,9 @@ +package net.leanix.vsm.gitlab.broker.logs.domain + +import java.util.UUID + +data class StatusLog( + val runId: UUID, + val status: LogStatus, + val message: String? +) diff --git a/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProviderTest.kt b/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProviderTest.kt new file mode 100644 index 0000000..4626308 --- /dev/null +++ b/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/adapter/feign/FeignLogProviderTest.kt @@ -0,0 +1,44 @@ +package net.leanix.vsm.gitlab.broker.logs.adapter.feign + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog +import net.leanix.vsm.gitlab.broker.logs.domain.LogLevel +import net.leanix.vsm.gitlab.broker.logs.domain.LogStatus +import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog +import org.junit.jupiter.api.Test +import java.util.UUID + +internal class FeignLogProviderTest { + + private val loggingClient = mockk() + private val statusLogClient = mockk() + private val feignLogProvider = FeignLogProvider(loggingClient, statusLogClient) + + @Test + fun `sending admin log should call correct client`() { + val adminLog = AdminLog( + runId = UUID.randomUUID(), + configurationId = UUID.randomUUID(), + subject = "dummy", + level = LogLevel.INFO, + message = "dummy" + ) + every { loggingClient.sendAdminLog(any()) } returns Unit + feignLogProvider.sendAdminLog(adminLog) + verify(exactly = 1) { loggingClient.sendAdminLog(any()) } + } + + @Test + fun `sending status log should call correct client`() { + val statusLog = StatusLog( + runId = UUID.randomUUID(), + status = LogStatus.IN_PROGRESS, + message = "Success" + ) + every { statusLogClient.sendStatusLog(any()) } returns Unit + feignLogProvider.sendStatusLog(statusLog) + verify(exactly = 1) { statusLogClient.sendStatusLog(any()) } + } +} diff --git a/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingServiceTest.kt b/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingServiceTest.kt new file mode 100644 index 0000000..fa83d62 --- /dev/null +++ b/src/test/kotlin/net/leanix/vsm/gitlab/broker/logs/application/LoggingServiceTest.kt @@ -0,0 +1,42 @@ +package net.leanix.vsm.gitlab.broker.logs.application + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import net.leanix.vsm.gitlab.broker.logs.domain.AdminLog +import net.leanix.vsm.gitlab.broker.logs.domain.LogLevel +import net.leanix.vsm.gitlab.broker.logs.domain.LogProvider +import net.leanix.vsm.gitlab.broker.logs.domain.LogStatus +import net.leanix.vsm.gitlab.broker.logs.domain.StatusLog +import org.junit.jupiter.api.Test +import java.util.UUID + +class LoggingServiceTest { + + private val logProvider = mockk() + private val loggingService = LoggingService(logProvider) + @Test + fun `sending status log should call correct client`() { + val statusLog = StatusLog( + runId = UUID.randomUUID(), + status = LogStatus.SUCCESSFUL, + message = "Success" + ) + every { logProvider.sendStatusLog(any()) } returns Unit + loggingService.sendStatusLog(statusLog) + verify(exactly = 1) { logProvider.sendStatusLog(statusLog) } + } + @Test + fun `sending admin log should call correct client`() { + val adminLog = AdminLog( + runId = UUID.randomUUID(), + configurationId = UUID.randomUUID(), + subject = "dummy", + level = LogLevel.INFO, + message = "dummy" + ) + every { logProvider.sendAdminLog(any()) } returns Unit + loggingService.sendAdminLog(adminLog) + verify(exactly = 1) { logProvider.sendAdminLog(adminLog) } + } +}