Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.leanix.githubagent.dto

data class RepositoryRequestDTO(
val installation: RepositoryRequestInstallationDTO,
val repositoryName: String,
val repositoryFullName: String,
)

data class RepositoryRequestInstallationDTO(
val id: Long,
val account: Account
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import java.util.concurrent.CountDownLatch

@Component
class BrokerStompSessionHandler(
private val artifactDownloadHandler: ArtifactDownloadHandler
private val artifactDownloadHandler: ArtifactDownloadHandler,
private val repositoryGetHandler: RepositoryGetHandler,
) : StompSessionHandlerAdapter() {
@Lazy
@Autowired
Expand All @@ -30,6 +31,7 @@ class BrokerStompSessionHandler(
isConnected = true
latch.countDown()
session.subscribe("/user/queue/message/artifact", artifactDownloadHandler)
session.subscribe("/user/queue/message/repository", repositoryGetHandler)
}

override fun handleException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package net.leanix.githubagent.handler

import net.leanix.githubagent.dto.Installation
import net.leanix.githubagent.dto.RepositoryRequestDTO
import net.leanix.githubagent.services.GitHubAuthenticationService
import net.leanix.githubagent.services.GitHubRepositoryService
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.messaging.simp.stomp.StompFrameHandler
import org.springframework.messaging.simp.stomp.StompHeaders
import org.springframework.stereotype.Component
import java.lang.reflect.Type

@Component
class RepositoryGetHandler(
@Lazy @Autowired
private val gitHubAuthenticationService: GitHubAuthenticationService,
@Lazy @Autowired
private val repositoryGetService: GitHubRepositoryService
) : StompFrameHandler {

private val logger = LoggerFactory.getLogger(RepositoryGetHandler::class.java)

override fun getPayloadType(headers: StompHeaders): Type {
return RepositoryRequestDTO::class.java
}

override fun handleFrame(headers: StompHeaders, payload: Any?) {
payload?.let {
val dto = payload as RepositoryRequestDTO
logger.info("Received repository get message from server for repo: ${dto.repositoryName}")
runCatching {
val installationToken = gitHubAuthenticationService.getInstallationToken(dto.installation.id.toInt())
repositoryGetService.fetchAndSendRepositoryAndManifest(
Installation(
id = dto.installation.id,
account = dto.installation.account,
mapOf(),
emptyList(),
),
dto.repositoryName,
dto.repositoryFullName,
installationToken
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package net.leanix.githubagent.services

import net.leanix.githubagent.dto.Installation
import net.leanix.githubagent.dto.ManifestFileAction
import net.leanix.githubagent.dto.ManifestFileUpdateDto
import net.leanix.githubagent.dto.RateLimitType
import net.leanix.githubagent.dto.RepositoryDto
import net.leanix.githubagent.handler.RateLimitHandler
import net.leanix.githubagent.shared.fileNameMatchRegex
import net.leanix.githubagent.shared.generateFullPath
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service

@Service
class GitHubRepositoryService(
@Lazy @Autowired
private val webSocketService: WebSocketService,
private val gitHubGraphQLService: GitHubGraphQLService,
@Lazy @Autowired
private val gitHubScanningService: GitHubScanningService,
@Lazy @Autowired
private val rateLimitHandler: RateLimitHandler,
) {
private val logger = LoggerFactory.getLogger(GitHubRepositoryService::class.java)

fun fetchAndSendRepositoryAndManifest(
installation: Installation,
repositoryName: String,
repositoryFullName: String,
installationToken: String
) {
kotlin.runCatching {
logger.info("Fetching repository details for: $repositoryFullName")
val repository = rateLimitHandler.executeWithRateLimitHandler(RateLimitType.GRAPHQL) {
gitHubGraphQLService.getRepository(
installation.account.login,
repositoryName,
installationToken
)
}
if (repository == null) {
logger.error("Failed to fetch repository details for: $repositoryFullName")
return
}
if (repository.archived) {
logger.info("Repository ${repository.fullName} is archived, skipping.")
return
}
logger.info("Sending repository details for: ${repository.fullName}")
webSocketService.sendMessage("/events/repository", repository)
fetchAndSendManifestFiles(installation, repository)
}.onFailure {
logger.error("Failed to process repository event: $repositoryFullName", it)
}
}

private fun fetchAndSendManifestFiles(installation: Installation, repositoryAdded: RepositoryDto) {
logger.info("Fetching manifest files for repository: ${repositoryAdded.fullName}")
val manifestFiles = gitHubScanningService.fetchManifestFiles(installation, repositoryAdded.name).getOrThrow()
val manifestContents = gitHubScanningService.fetchManifestContents(
installation,
manifestFiles,
repositoryAdded.name,
repositoryAdded.defaultBranch
).getOrThrow()

if (manifestContents.isEmpty()) {
logger.info("No manifest files found for repository: ${repositoryAdded.fullName}")
return
}
manifestContents.forEach {
logger.info("Sending manifest file content for: ${it.path} in repository: ${repositoryAdded.fullName}")
webSocketService.sendMessage(
"/events/manifestFile",
ManifestFileUpdateDto(
repositoryAdded.fullName,
ManifestFileAction.ADDED,
it.content,
generateFullPath(repositoryAdded.defaultBranch, fileNameMatchRegex.replace(it.path, ""))
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@ package net.leanix.githubagent.services
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import net.leanix.githubagent.client.GitHubClient
import net.leanix.githubagent.dto.Installation
import net.leanix.githubagent.dto.InstallationEventPayload
import net.leanix.githubagent.dto.InstallationRepositoriesEventPayload
import net.leanix.githubagent.dto.InstallationRepositoriesRepository
import net.leanix.githubagent.dto.ManifestFileAction
import net.leanix.githubagent.dto.ManifestFileUpdateDto
import net.leanix.githubagent.dto.PushEventCommit
import net.leanix.githubagent.dto.PushEventPayload
import net.leanix.githubagent.dto.RateLimitType
import net.leanix.githubagent.dto.RepositoryDto
import net.leanix.githubagent.dto.toInstallation
import net.leanix.githubagent.exceptions.JwtTokenNotFound
import net.leanix.githubagent.handler.RateLimitHandler
import net.leanix.githubagent.shared.INSTALLATION_LABEL
import net.leanix.githubagent.shared.INSTALLATION_REPOSITORIES
import net.leanix.githubagent.shared.MANIFEST_FILE_NAME
Expand All @@ -37,7 +32,7 @@ class WebhookEventService(
@Value("\${webhookEventService.waitingTime}") private val waitingTime: Long,
private val gitHubClient: GitHubClient,
private val gitHubEnterpriseService: GitHubEnterpriseService,
private val rateLimitHandler: RateLimitHandler,
private val gitHubRepositoryService: GitHubRepositoryService,
) {

private val logger = LoggerFactory.getLogger(WebhookEventService::class.java)
Expand All @@ -50,7 +45,7 @@ class WebhookEventService(
INSTALLATION_REPOSITORIES -> handleInstallationRepositories(payload)
else -> {
logger.info("Sending event of type: $eventType")
webSocketService.sendMessage("/events/$eventType", payload)
webSocketService.sendMessage("/events/other/$eventType", payload)
}
}
}
Expand Down Expand Up @@ -222,61 +217,12 @@ class WebhookEventService(
installationRepositoriesEventPayload.repositoriesAdded
.forEach { repositoryAdded ->
logger.info("Processing repository: ${repositoryAdded.fullName}")
processRepository(installation.toInstallation(), repositoryAdded, installationToken)
}
}

private fun processRepository(
installation: Installation,
repositoryAdded: InstallationRepositoriesRepository,
installationToken: String
) {
kotlin.runCatching {
logger.info("Fetching repository details for: ${repositoryAdded.fullName}")
val repository = rateLimitHandler.executeWithRateLimitHandler(RateLimitType.GRAPHQL) {
gitHubGraphQLService.getRepository(
installation.account.login,
gitHubRepositoryService.fetchAndSendRepositoryAndManifest(
installation.toInstallation(),
repositoryAdded.name,
repositoryAdded.fullName,
installationToken
)
}
if (repository == null) {
logger.error("Failed to fetch repository details for: ${repositoryAdded.fullName}")
return
}
if (repository.archived) {
logger.info("Repository ${repository.fullName} is archived, skipping.")
return
}
logger.info("Sending repository details for: ${repository.fullName}")
webSocketService.sendMessage("/events/repository", repository)
fetchAndSendManifestFiles(installation, repository)
}.onFailure {
logger.error("Failed to process repository event: ${repositoryAdded.fullName}", it)
}
}

private fun fetchAndSendManifestFiles(installation: Installation, repositoryAdded: RepositoryDto) {
logger.info("Fetching manifest files for repository: ${repositoryAdded.fullName}")
val manifestFiles = gitHubScanningService.fetchManifestFiles(installation, repositoryAdded.name).getOrThrow()
val manifestContents = gitHubScanningService.fetchManifestContents(
installation,
manifestFiles,
repositoryAdded.name,
repositoryAdded.defaultBranch
).getOrThrow()

manifestContents.forEach {
logger.info("Sending manifest file content for: ${it.path} in repository: ${repositoryAdded.fullName}")
webSocketService.sendMessage(
"/events/manifestFile",
ManifestFileUpdateDto(
repositoryAdded.fullName,
ManifestFileAction.ADDED,
it.content,
generateFullPath(repositoryAdded.defaultBranch, fileNameMatchRegex.replace(it.path, ""))
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.leanix.githubagent.handler

import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import net.leanix.githubagent.dto.Account
import net.leanix.githubagent.dto.Installation
import net.leanix.githubagent.dto.RepositoryRequestDTO
import net.leanix.githubagent.dto.RepositoryRequestInstallationDTO
import net.leanix.githubagent.services.GitHubAuthenticationService
import net.leanix.githubagent.services.GitHubRepositoryService
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.messaging.simp.stomp.StompHeaders

class RepositoryGetHandlerTest {

private val gitHubAuthenticationService = mockk<GitHubAuthenticationService>()

private val gitHubRepositoryService = mockk<GitHubRepositoryService>()

private val repositoryGetHandler = RepositoryGetHandler(
gitHubAuthenticationService,
gitHubRepositoryService
)

@BeforeEach
fun setup() {
MockKAnnotations.init(repositoryGetHandler)
}

@Test
fun `it should receive message from server and send repository data and manifest files`() {
// given
every { gitHubAuthenticationService.getInstallationToken(any()) } returns "token"

// when
repositoryGetHandler.handleFrame(
StompHeaders(),
RepositoryRequestDTO(
installation = RepositoryRequestInstallationDTO(1, Account("account")),
repositoryName = "repoName",
repositoryFullName = "repoFullName"
)
)

// then
verify {
gitHubRepositoryService.fetchAndSendRepositoryAndManifest(
installation = Installation(1, Account("account"), mapOf(), listOf()),
repositoryName = "repoName",
repositoryFullName = "repoFullName",
installationToken = "token"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class WebhookEventServiceTest {

webhookEventService.consumeWebhookEvent("OTHER", payload)

verify(exactly = 1) { webSocketService.sendMessage("/events/OTHER", payload) }
verify(exactly = 1) { webSocketService.sendMessage("/events/other/OTHER", payload) }
}

@Test
Expand Down
Loading