Skip to content

Commit aacc4bf

Browse files
Merge pull request #89 from leanix/feature/CID-3627/Support-repo-created-event
Feature/cid 3627/support repo created event
2 parents d8d57e8 + 173b6e8 commit aacc4bf

File tree

7 files changed

+316
-4
lines changed

7 files changed

+316
-4
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package net.leanix.githubagent.dto
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
6+
@JsonIgnoreProperties(ignoreUnknown = true)
7+
data class InstallationRepositoriesEventPayload(
8+
val installation: InstallationRepositoriesInstallation,
9+
@JsonProperty("repositories_added") val repositoriesAdded: List<InstallationRepositoriesRepository>
10+
)
11+
12+
@JsonIgnoreProperties(ignoreUnknown = true)
13+
data class InstallationRepositoriesInstallation(
14+
val id: Int,
15+
val account: InstallationEventRepositoriesInstallationAccount
16+
)
17+
18+
@JsonIgnoreProperties(ignoreUnknown = true)
19+
data class InstallationEventRepositoriesInstallationAccount(
20+
val login: String,
21+
val id: Int
22+
)
23+
24+
@JsonIgnoreProperties(ignoreUnknown = true)
25+
data class InstallationRepositoriesRepository(
26+
val id: Int,
27+
@JsonProperty("node_id") val nodeId: String,
28+
val name: String,
29+
@JsonProperty("full_name") val fullName: String,
30+
val private: Boolean
31+
)
32+
33+
fun InstallationRepositoriesInstallation.toInstallation(): Installation {
34+
return Installation(
35+
id = id.toLong(),
36+
account = Account(login = account.login),
37+
events = emptyList(),
38+
permissions = emptyMap()
39+
)
40+
}

src/main/kotlin/net/leanix/githubagent/services/GitHubGraphQLService.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import net.leanix.githubagent.dto.PagedRepositories
66
import net.leanix.githubagent.dto.RepositoryDto
77
import net.leanix.githubagent.exceptions.GraphQLApiException
88
import net.leanix.githubagent.graphql.data.GetRepositories
9+
import net.leanix.githubagent.graphql.data.GetRepository
910
import net.leanix.githubagent.graphql.data.GetRepositoryManifestContent
1011
import net.leanix.githubagent.interceptor.RateLimitInterceptor
1112
import org.slf4j.LoggerFactory
@@ -94,6 +95,46 @@ class GitHubGraphQLService(
9495
return (result.data?.repository?.manifestFile as? RepositoryManifestBlob)?.text
9596
}
9697

98+
fun getRepository(
99+
owner: String,
100+
repositoryName: String,
101+
token: String
102+
): RepositoryDto? {
103+
val client = buildGitHubGraphQLClient(token)
104+
105+
val query = GetRepository(
106+
GetRepository.Variables(
107+
owner = owner,
108+
repositoryName = repositoryName
109+
)
110+
)
111+
112+
val result = runBlocking {
113+
client.execute(query)
114+
}
115+
116+
val repository = result.data?.repositoryOwner?.repository
117+
118+
if (repository == null) {
119+
logger.error("Error getting repository: ${result.errors}")
120+
throw GraphQLApiException(result.errors!!)
121+
}
122+
123+
return RepositoryDto(
124+
id = repository.id,
125+
name = repository.name,
126+
organizationName = repository.owner.login,
127+
description = repository.description,
128+
url = repository.url,
129+
defaultBranch = repository.defaultBranchRef?.name,
130+
archived = repository.isArchived,
131+
visibility = repository.visibility,
132+
updatedAt = repository.updatedAt,
133+
languages = repository.languages!!.nodes!!.map { language -> language!!.name },
134+
topics = repository.repositoryTopics.nodes!!.map { topic -> topic!!.topic.name },
135+
)
136+
}
137+
97138
private fun buildGitHubGraphQLClient(
98139
token: String
99140
) =

src/main/kotlin/net/leanix/githubagent/services/GitHubScanningService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class GitHubScanningService(
147147
)
148148
}
149149

150-
private fun fetchManifestFiles(installation: Installation, repositoryName: String) = runCatching {
150+
fun fetchManifestFiles(installation: Installation, repositoryName: String) = runCatching {
151151
val installationToken = cachingService.get("installationToken:${installation.id}").toString()
152152
val manifestFiles = rateLimitHandler.executeWithRateLimitHandler(RateLimitType.SEARCH) {
153153
gitHubClient.searchManifestFiles(
@@ -158,7 +158,7 @@ class GitHubScanningService(
158158
}
159159
manifestFiles.items.filter { it.name.lowercase() == MANIFEST_FILE_NAME }
160160
}
161-
private fun fetchManifestContents(
161+
fun fetchManifestContents(
162162
installation: Installation,
163163
items: List<ItemResponse>,
164164
repositoryName: String,

src/main/kotlin/net/leanix/githubagent/services/WebhookEventService.kt

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@ package net.leanix.githubagent.services
33
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
44
import com.fasterxml.jackson.module.kotlin.readValue
55
import net.leanix.githubagent.client.GitHubClient
6+
import net.leanix.githubagent.dto.Installation
67
import net.leanix.githubagent.dto.InstallationEventPayload
8+
import net.leanix.githubagent.dto.InstallationRepositoriesEventPayload
9+
import net.leanix.githubagent.dto.InstallationRepositoriesRepository
710
import net.leanix.githubagent.dto.ManifestFileAction
811
import net.leanix.githubagent.dto.ManifestFileUpdateDto
912
import net.leanix.githubagent.dto.PushEventCommit
1013
import net.leanix.githubagent.dto.PushEventPayload
14+
import net.leanix.githubagent.dto.RateLimitType
15+
import net.leanix.githubagent.dto.RepositoryDto
16+
import net.leanix.githubagent.dto.toInstallation
1117
import net.leanix.githubagent.exceptions.JwtTokenNotFound
18+
import net.leanix.githubagent.handler.RateLimitHandler
1219
import net.leanix.githubagent.shared.INSTALLATION_LABEL
20+
import net.leanix.githubagent.shared.INSTALLATION_REPOSITORIES
1321
import net.leanix.githubagent.shared.MANIFEST_FILE_NAME
1422
import net.leanix.githubagent.shared.WORKFLOW_RUN_EVENT
1523
import net.leanix.githubagent.shared.fileNameMatchRegex
@@ -18,6 +26,7 @@ import org.slf4j.LoggerFactory
1826
import org.springframework.beans.factory.annotation.Value
1927
import org.springframework.stereotype.Service
2028

29+
@SuppressWarnings("TooManyFunctions")
2130
@Service
2231
class WebhookEventService(
2332
private val webSocketService: WebSocketService,
@@ -29,7 +38,8 @@ class WebhookEventService(
2938
@Value("\${webhookEventService.waitingTime}") private val waitingTime: Long,
3039
private val gitHubClient: GitHubClient,
3140
private val gitHubEnterpriseService: GitHubEnterpriseService,
32-
private val workflowRunService: WorkflowRunService
41+
private val workflowRunService: WorkflowRunService,
42+
private val rateLimitHandler: RateLimitHandler,
3343
) {
3444

3545
private val logger = LoggerFactory.getLogger(WebhookEventService::class.java)
@@ -39,6 +49,7 @@ class WebhookEventService(
3949
when (eventType.uppercase()) {
4050
"PUSH" -> handlePushEvent(payload)
4151
"INSTALLATION" -> handleInstallationEvent(payload)
52+
INSTALLATION_REPOSITORIES -> handleInstallationRepositories(payload)
4253
WORKFLOW_RUN_EVENT -> workflowRunService.consumeWebhookPayload(payload)
4354
else -> {
4455
logger.info("Sending event of type: $eventType")
@@ -204,4 +215,71 @@ class WebhookEventService(
204215
"root folder"
205216
}
206217
}
218+
219+
private val handleInstallationRepositories: (String) -> Unit = { payload ->
220+
logger.info("Handling installation repositories event")
221+
val installationRepositoriesEventPayload: InstallationRepositoriesEventPayload = objectMapper.readValue(payload)
222+
val installation = installationRepositoriesEventPayload.installation
223+
val installationToken = gitHubAuthenticationService.getInstallationToken(installation.id)
224+
225+
installationRepositoriesEventPayload.repositoriesAdded
226+
.forEach { repositoryAdded ->
227+
logger.info("Processing repository: ${repositoryAdded.fullName}")
228+
processRepository(installation.toInstallation(), repositoryAdded, installationToken)
229+
}
230+
}
231+
232+
private fun processRepository(
233+
installation: Installation,
234+
repositoryAdded: InstallationRepositoriesRepository,
235+
installationToken: String
236+
) {
237+
kotlin.runCatching {
238+
logger.info("Fetching repository details for: ${repositoryAdded.fullName}")
239+
val repository = rateLimitHandler.executeWithRateLimitHandler(RateLimitType.GRAPHQL) {
240+
gitHubGraphQLService.getRepository(
241+
installation.account.login,
242+
repositoryAdded.name,
243+
installationToken
244+
)
245+
}
246+
if (repository == null) {
247+
logger.error("Failed to fetch repository details for: ${repositoryAdded.fullName}")
248+
return
249+
}
250+
if (repository.archived) {
251+
logger.info("Repository ${repository.fullName} is archived, skipping.")
252+
return
253+
}
254+
logger.info("Sending repository details for: ${repository.fullName}")
255+
webSocketService.sendMessage("/events/repository", repository)
256+
fetchAndSendManifestFiles(installation, repository)
257+
}.onFailure {
258+
logger.error("Failed to process repository event: ${repositoryAdded.fullName}", it)
259+
}
260+
}
261+
262+
private fun fetchAndSendManifestFiles(installation: Installation, repositoryAdded: RepositoryDto) {
263+
logger.info("Fetching manifest files for repository: ${repositoryAdded.fullName}")
264+
val manifestFiles = gitHubScanningService.fetchManifestFiles(installation, repositoryAdded.name).getOrThrow()
265+
val manifestContents = gitHubScanningService.fetchManifestContents(
266+
installation,
267+
manifestFiles,
268+
repositoryAdded.name,
269+
repositoryAdded.defaultBranch
270+
).getOrThrow()
271+
272+
manifestContents.forEach {
273+
logger.info("Sending manifest file content for: ${it.path} in repository: ${repositoryAdded.fullName}")
274+
webSocketService.sendMessage(
275+
"/events/manifestFile",
276+
ManifestFileUpdateDto(
277+
repositoryAdded.fullName,
278+
ManifestFileAction.ADDED,
279+
it.content,
280+
generateFullPath(repositoryAdded.defaultBranch, fileNameMatchRegex.replace(it.path, ""))
281+
)
282+
)
283+
}
284+
}
207285
}

src/main/kotlin/net/leanix/githubagent/shared/Constants.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ const val APP_NAME_TOPIC = "appName"
55
const val LOGS_TOPIC = "logs"
66
const val MANIFEST_FILE_NAME = "leanix.yaml"
77
const val WORKFLOW_RUN_EVENT = "WORKFLOW_RUN"
8+
const val INSTALLATION_REPOSITORIES = "INSTALLATION_REPOSITORIES"
89

910
val SUPPORTED_EVENT_TYPES = listOf(
1011
"REPOSITORY",
1112
"PUSH",
1213
"ORGANIZATION",
1314
"INSTALLATION",
14-
WORKFLOW_RUN_EVENT
15+
WORKFLOW_RUN_EVENT,
16+
INSTALLATION_REPOSITORIES
1517
)
1618

1719
val fileNameMatchRegex = Regex("/?$MANIFEST_FILE_NAME\$", RegexOption.IGNORE_CASE)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
query GetRepository($owner: String!, $repositoryName: String!) {
2+
repositoryOwner(login: $owner){
3+
repository(name: $repositoryName) {
4+
id
5+
name
6+
description
7+
url
8+
defaultBranchRef{
9+
name
10+
}
11+
isArchived
12+
visibility
13+
updatedAt
14+
owner{
15+
login
16+
}
17+
languages(first: 25) {
18+
nodes {
19+
name
20+
}
21+
}
22+
repositoryTopics(first:25) {
23+
nodes {
24+
topic {
25+
name
26+
}
27+
}
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)