diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/application/AssignmentService.kt similarity index 94% rename from src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt rename to src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/application/AssignmentService.kt index 7d3d128..328950c 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/applicaiton/AssignmentService.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/application/AssignmentService.kt @@ -1,4 +1,4 @@ -package net.leanix.vsm.gitlab.broker.connector.applicaiton +package net.leanix.vsm.gitlab.broker.connector.application import net.leanix.vsm.gitlab.broker.connector.domain.AssignmentProvider import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt index dcd681a..1b77d15 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/runner/InitialStateRunner.kt @@ -1,7 +1,8 @@ package net.leanix.vsm.gitlab.broker.connector.runner -import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService +import net.leanix.vsm.gitlab.broker.connector.application.AssignmentService import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache +import net.leanix.vsm.gitlab.broker.webhook.application.WebhookService import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.boot.ApplicationArguments @@ -11,12 +12,18 @@ import org.springframework.stereotype.Component @Component class InitialStateRunner( private val assignmentService: AssignmentService, + private val webhookService: WebhookService ) : ApplicationRunner { private val logger: Logger = LoggerFactory.getLogger(InitialStateRunner::class.java) override fun run(args: ApplicationArguments?) { logger.info("Started to get initial state") + fetchAssignments() + setupWebhook() + } + + private fun fetchAssignments() { runCatching { assignmentService.getAssignments()?.forEach { assignment -> logger.info( @@ -30,4 +37,14 @@ class InitialStateRunner( logger.error("Failed to get initial state", e) } } + + private fun setupWebhook() { + runCatching { + webhookService.registerWebhook() + }.onSuccess { + logger.info("webhook registered successfully") + }.onFailure { + logger.info("webhook registration failed", it) + } + } } diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt index 068d214..7935851 100644 --- a/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatScheduler.kt @@ -1,7 +1,7 @@ package net.leanix.vsm.gitlab.broker.connector.scheduler import net.leanix.vsm.gitlab.broker.connector.adapter.feign.VsmClient -import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService +import net.leanix.vsm.gitlab.broker.connector.application.AssignmentService import net.leanix.vsm.gitlab.broker.shared.cache.AssignmentsCache import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Scheduled diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookClient.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookClient.kt new file mode 100644 index 0000000..1036bab --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookClient.kt @@ -0,0 +1,35 @@ +package net.leanix.vsm.gitlab.broker.webhook.adapter.feign + +import net.leanix.vsm.gitlab.broker.webhook.domain.GitlabWebhookDto +import org.springframework.cloud.openfeign.FeignClient +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestParam + +@FeignClient( + name = "gitlabWebhookClient", + url = "\${leanix.gitlab.base-url}", + configuration = [GitlabWebhookFeignClientConfiguration::class] +) +interface GitlabWebhookClient { + + @GetMapping("/hooks") + fun getAllWebhooks(): List + + @DeleteMapping("/hooks/{webhookId}") + fun deleteWebhook(@PathVariable("webhookId") webhookId: Int) + + @Suppress("LongParameterList") + @PostMapping("/hooks") + fun createWebhook( + @RequestParam("url") url: String, + @RequestParam("token") token: String, + @RequestParam("push_events") receivePushEvents: Boolean, + @RequestParam("tag_push_events") receiveTagPushEvents: Boolean, + @RequestParam("merge_requests_events") receiveMergeRequestEvents: Boolean, + @RequestParam("repository_update_events") receiveRepositoryUpdateEvents: Boolean, + @RequestParam("enable_ssl_verification") enableSSLVerification: Boolean, + ): GitlabWebhookDto +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookFeignClientConfiguration.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookFeignClientConfiguration.kt new file mode 100644 index 0000000..723c655 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookFeignClientConfiguration.kt @@ -0,0 +1,19 @@ +package net.leanix.vsm.gitlab.broker.webhook.adapter.feign + +import feign.RequestInterceptor +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class GitlabWebhookFeignClientConfiguration( + + @Value("\${leanix.gitlab.access-token}") private val gitlabAccessToken: String +) { + @Bean + fun requestInterceptor(): RequestInterceptor { + return RequestInterceptor { + it.header("PRIVATE-TOKEN", gitlabAccessToken) + } + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookProvider.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookProvider.kt new file mode 100644 index 0000000..d020016 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/adapter/feign/GitlabWebhookProvider.kt @@ -0,0 +1,54 @@ +package net.leanix.vsm.gitlab.broker.webhook.adapter.feign + +import net.leanix.vsm.gitlab.broker.connector.application.AssignmentService +import net.leanix.vsm.gitlab.broker.webhook.domain.GitlabWebhookDto +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component + +interface WebhookProvider { + fun getAllWebhooks(): List + fun deleteWebhook(webhookId: Int) + fun createWebhook(): GitlabWebhookDto +} + +@Component +class GitlabWebhookProvider( + private val webhookClient: GitlabWebhookClient, + @Value("\${leanix.gitlab.webhook-url}") private val gitlabWebhookUrl: String, + @Value("\${leanix.gitlab.leanix-id}") private val leanixId: String +) : WebhookProvider { + + private val logger = LoggerFactory.getLogger(AssignmentService::class.java) + override fun getAllWebhooks(): List { + return kotlin.runCatching { + webhookClient.getAllWebhooks() + }.onSuccess { + logger.info("Webhooks fetched. size: ${it.size}") + }.getOrThrow() + } + + override fun deleteWebhook(webhookId: Int) { + return kotlin.runCatching { + webhookClient.deleteWebhook(webhookId) + }.onSuccess { + logger.info("Webhooks deleted for id: $webhookId") + }.getOrThrow() + } + + override fun createWebhook(): GitlabWebhookDto { + return kotlin.runCatching { + webhookClient.createWebhook( + url = gitlabWebhookUrl, + token = leanixId, + receivePushEvents = true, + receiveTagPushEvents = false, + receiveMergeRequestEvents = true, + receiveRepositoryUpdateEvents = true, + enableSSLVerification = false + ) + }.onSuccess { + logger.info("Webhook created with id ${it.id}") + }.getOrThrow() + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/WebhookService.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/WebhookService.kt new file mode 100644 index 0000000..2d0e197 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/WebhookService.kt @@ -0,0 +1,25 @@ +package net.leanix.vsm.gitlab.broker.webhook.application + +import net.leanix.vsm.gitlab.broker.webhook.adapter.feign.WebhookProvider +import net.leanix.vsm.gitlab.broker.webhook.domain.GitlabWebhookDto +import org.springframework.stereotype.Service + +interface WebhookService { + fun registerWebhook(): GitlabWebhookDto +} + +@Service +class GitlabWebhookServiceImpl( + private val webhookProvider: WebhookProvider +) : WebhookService { + + override fun registerWebhook(): GitlabWebhookDto { + val webhook = webhookProvider.createWebhook() + + webhookProvider.getAllWebhooks() + .filter { it.id != webhook.id } + .forEach { webhookProvider.deleteWebhook(it.id) } + + return webhook + } +} diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/CreateGitlabWebhookRequestDto.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/CreateGitlabWebhookRequestDto.kt new file mode 100644 index 0000000..bb57f41 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/CreateGitlabWebhookRequestDto.kt @@ -0,0 +1,18 @@ +package net.leanix.vsm.gitlab.broker.webhook.domain + +import com.nimbusds.jose.shaded.gson.annotations.SerializedName + +data class CreateGitlabWebhookRequestDto( + val url: String, + val token: String, + @SerializedName("push_events") + val pushEvents: Boolean, + @SerializedName("tag_push_events") + val tagPushEvents: Boolean, + @SerializedName("merge_requests_events") + val mergeRequestsEvents: Boolean, + @SerializedName("repository_update_events") + val repositoryUpdateEvents: Boolean, + @SerializedName("enable_ssl_verification") + val enableSSLVerification: Boolean, +) diff --git a/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/GitlabWebhookDto.kt b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/GitlabWebhookDto.kt new file mode 100644 index 0000000..1da3258 --- /dev/null +++ b/src/main/kotlin/net/leanix/vsm/gitlab/broker/webhook/domain/GitlabWebhookDto.kt @@ -0,0 +1,21 @@ +package net.leanix.vsm.gitlab.broker.webhook.domain + +import com.fasterxml.jackson.annotation.JsonProperty +import java.util.Date + +data class GitlabWebhookDto( + val id: Int, + val url: String, + @JsonProperty("created_at") + val createdAt: Date, + @JsonProperty("push_events") + val pushEvents: Boolean, + @JsonProperty("tag_push_events") + val tagPushEvents: Boolean, + @JsonProperty("merge_requests_events") + val mergeRequestsEvents: Boolean, + @JsonProperty("repository_update_events") + val repositoryUpdateEvents: Boolean, + @JsonProperty("enable_ssl_verification") + val enableSSLVerification: Boolean, +) diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 4537d90..6a56abb 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -7,6 +7,8 @@ leanix: base-url: http://localhost:8080 auth: access-token-uri: https://test-app-1.leanix.net/services/mtm/v1 + gitlab: + leanix-id: ${LEANIX_ID:dummy} server: port: 8082 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index decc64b..5152b48 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -9,6 +9,11 @@ leanix: base-url: ${leanix.vsm.base-url}/vsm-events-broker auth: access-token-uri: ${leanix.base-url}/mtm/v1 + gitlab: + base-url: ${GITLAB_URL} + access-token: ${GITLAB_TOKEN} + webhook-url: ${GITLAB_WEBHOOK_URL} + leanix-id: ${LEANIX_ID} server: port: 8082 \ No newline at end of file diff --git a/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt b/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt index d75226c..95b38fb 100644 --- a/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt +++ b/src/test/kotlin/net/leanix/vsm/gitlab/broker/connector/scheduler/HeartbeatSchedulerTest.kt @@ -1,7 +1,7 @@ package net.leanix.vsm.gitlab.broker.connector.scheduler import net.leanix.vsm.gitlab.broker.connector.adapter.feign.VsmClient -import net.leanix.vsm.gitlab.broker.connector.applicaiton.AssignmentService +import net.leanix.vsm.gitlab.broker.connector.application.AssignmentService import net.leanix.vsm.gitlab.broker.connector.domain.GitLabAssignment import net.leanix.vsm.gitlab.broker.connector.domain.GitLabConfiguration import net.leanix.vsm.gitlab.broker.connector.domain.GitLabHeartbeatResponse diff --git a/src/test/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/GitlabWebhookServiceImplTest.kt b/src/test/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/GitlabWebhookServiceImplTest.kt new file mode 100644 index 0000000..d9ebc14 --- /dev/null +++ b/src/test/kotlin/net/leanix/vsm/gitlab/broker/webhook/application/GitlabWebhookServiceImplTest.kt @@ -0,0 +1,44 @@ +package net.leanix.vsm.gitlab.broker.webhook.application + +import net.leanix.vsm.gitlab.broker.webhook.adapter.feign.WebhookProvider +import net.leanix.vsm.gitlab.broker.webhook.domain.GitlabWebhookDto +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import java.util.Date + +class GitlabWebhookServiceImplTest { + + private val webhookProvider = mock(WebhookProvider::class.java) + private val subject = GitlabWebhookServiceImpl(webhookProvider) + + @Test + fun `given a new one webhook is created successfully when registerWebhook then delete all other webhooks`() { + `when`(webhookProvider.createWebhook()).thenReturn(dummyGitlabWebhookDto(id = 1)) + `when`(webhookProvider.getAllWebhooks()).thenReturn( + listOf( + dummyGitlabWebhookDto(id = 1), + dummyGitlabWebhookDto(id = 2) + ) + ) + + subject.registerWebhook() + + verify(webhookProvider, times(0)).deleteWebhook(eq(1)) + verify(webhookProvider).deleteWebhook(eq(2)) + } +} + +fun dummyGitlabWebhookDto(id: Int) = GitlabWebhookDto( + id = id, + url = "https://gitlab.example.com/hook", + createdAt = Date(), + pushEvents = true, + tagPushEvents = false, + mergeRequestsEvents = true, + repositoryUpdateEvents = true, + enableSSLVerification = false +) diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index f3cb85c..30bfca9 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -8,4 +8,9 @@ leanix: events-broker: base-url: http://localhost:${wiremock.server.port:6666} auth: - access-token-uri: ${leanix.base-url}/mtm/v1 \ No newline at end of file + access-token-uri: ${leanix.base-url}/mtm/v1 + gitlab: + base-url: dummy + access-token: dummy + webhook-url: dummy + leanix-id: dummy \ No newline at end of file