Skip to content

Feature/cid 2731/establish ws connection on startup #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
60 changes: 32 additions & 28 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,45 +1,38 @@
.idea
.vscode

# Connectors
**/node_modules
**/dist
**/.env
**/combined.log
**/error.log
**/coverage

### OSX ###
.DS_Store

### Server ###
HELP.md
.gradle
build/
server/HELP.md
server/.gradle
server/build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
!**/server/src/main/**/build/
!**/server/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
server/.apt_generated
server/.classpath
server/.factorypath
server/.project
server/.settings
server/.springBeans
server/.sts4-cache
server/bin/
!**/server/src/main/**/bin/
!**/server/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
!**/server/src/main/**/out/
!**/server/src/test/**/out/


### Develpment files ###
db-data/

### K8s ###
k8s/*.d.ts
Expand All @@ -51,4 +44,15 @@ k8s/imports
k8s/k8s-deployment
k8s/__snapshots__

### connectors
**/node_modules

# build
.gradle/
build/

### local files ###
**/application-local.yaml
**/application-local.yml
**/bom.json
**/bom.xml
6 changes: 5 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("com.github.ben-manes.caffeine:caffeine:2.8.8")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
implementation("org.springframework.boot:spring-boot-starter-websocket")
implementation("io.github.resilience4j:resilience4j-spring-boot3:2.1.0")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.17.1")

Expand All @@ -41,7 +43,9 @@ dependencies {
implementation("io.jsonwebtoken:jjwt-jackson:0.11.2")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.mockk:mockk:1.12.0")
testImplementation("com.ninja-squad:springmockk:4.0.2"){
exclude(module = "mockito-core")
}
}

configurations.all {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import org.springframework.cloud.openfeign.EnableFeignClients

@SpringBootApplication
@EnableFeignClients
@EnableConfigurationProperties(value = [net.leanix.githubagent.config.GitHubEnterpriseProperties::class])
@EnableConfigurationProperties(
value = [
net.leanix.githubagent.config.GitHubEnterpriseProperties::class,
net.leanix.githubagent.config.LeanIXProperties::class
]
)
class GitHubAgentApplication

fun main() {
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/net/leanix/githubagent/client/AuthClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.leanix.githubagent.client

import net.leanix.githubagent.dto.JwtDto
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.http.HttpHeaders.AUTHORIZATION
import org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader

@FeignClient(
name = "authentication",
url = "\${leanix.auth.access-token-uri}",
)
fun interface AuthClient {

@PostMapping(value = ["/oauth2/token"], consumes = [APPLICATION_FORM_URLENCODED_VALUE])
fun getToken(
@RequestHeader(name = AUTHORIZATION) authorization: String,
@RequestBody body: String,
): JwtDto
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import org.springframework.stereotype.Component

@Component
class AgentSetupValidation(
private val githubEnterpriseProperties: GitHubEnterpriseProperties
private val gitHubEnterpriseProperties: GitHubEnterpriseProperties
) {

@PostConstruct
fun validateConfiguration() {
val missingProperties = mutableListOf<String>()

if (githubEnterpriseProperties.baseUrl.isBlank()) {
if (gitHubEnterpriseProperties.baseUrl.isBlank()) {
missingProperties.add("GITHUB_ENTERPRISE_BASE_URL")
}
if (githubEnterpriseProperties.githubAppId.isBlank()) {
if (gitHubEnterpriseProperties.gitHubAppId.isBlank()) {
missingProperties.add("GITHUB_ENTERPRISE_GITHUB_APP_ID")
}
if (githubEnterpriseProperties.pemFile.isBlank()) {
if (gitHubEnterpriseProperties.pemFile.isBlank()) {
missingProperties.add("GITHUB_ENTERPRISE_PEM_FILE")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(prefix = "github-enterprise")
data class GitHubEnterpriseProperties(
val baseUrl: String,
val githubAppId: String,
val gitHubAppId: String,
val pemFile: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.leanix.githubagent.config

import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties(prefix = "leanix")
data class LeanIXProperties(
val wsBaseUrl: String,
val auth: Auth
) {
data class Auth(
val accessTokenUri: String,
val apiUserToken: String
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.leanix.githubagent.config

import com.fasterxml.jackson.databind.ObjectMapper
import io.github.resilience4j.retry.annotation.Retry
import net.leanix.githubagent.handler.BrokerStompSessionHandler
import net.leanix.githubagent.services.AuthService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.converter.MappingJackson2MessageConverter
import org.springframework.messaging.simp.stomp.StompHeaders
import org.springframework.messaging.simp.stomp.StompSession
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
import org.springframework.web.socket.WebSocketHttpHeaders
import org.springframework.web.socket.client.standard.StandardWebSocketClient
import org.springframework.web.socket.messaging.WebSocketStompClient
import org.springframework.web.socket.sockjs.client.SockJsClient
import org.springframework.web.socket.sockjs.client.WebSocketTransport

@Configuration
class WebSocketClientConfig(
private val brokerStompSessionHandler: BrokerStompSessionHandler,
private val objectMapper: ObjectMapper,
private val authService: AuthService,
private val leanIXProperties: LeanIXProperties,
private val gitHubEnterpriseProperties: GitHubEnterpriseProperties
) {

@Retry(name = "ws-init-session")
fun initSession(): StompSession {
val headers = WebSocketHttpHeaders()
val stompHeaders = StompHeaders()
stompHeaders["Authorization"] = "Bearer ${authService.getBearerToken()}"
stompHeaders["GitHub-Enterprise-URL"] = gitHubEnterpriseProperties.baseUrl
stompHeaders["GiHub-Enterprise-App-Id"] = gitHubEnterpriseProperties.gitHubAppId
return stompClient().connectAsync(
leanIXProperties.wsBaseUrl,
headers,
stompHeaders,
brokerStompSessionHandler,
).get()
}

@Bean
fun stompClient(): WebSocketStompClient {
val jsonConverter = MappingJackson2MessageConverter()
jsonConverter.objectMapper = objectMapper

val simpleWebSocketClient = StandardWebSocketClient()
val transports = listOf(WebSocketTransport(simpleWebSocketClient))
val sockJsClient = SockJsClient(transports)
val stompClient = WebSocketStompClient(sockJsClient)
stompClient.messageConverter = jsonConverter
val scheduler = ThreadPoolTaskScheduler()
scheduler.initialize()
stompClient.taskScheduler = scheduler
return stompClient
}
}
14 changes: 14 additions & 0 deletions src/main/kotlin/net/leanix/githubagent/dto/JwtDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.leanix.githubagent.dto

import com.fasterxml.jackson.annotation.JsonProperty

data class JwtDto(
val scope: String,
val expired: Boolean,
@JsonProperty("access_token")
val accessToken: String,
@JsonProperty("token_type")
val tokenType: String,
@JsonProperty("expired_in")
val expiredIn: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.leanix.githubagent.handler

import org.slf4j.LoggerFactory
import org.springframework.messaging.simp.stomp.StompCommand
import org.springframework.messaging.simp.stomp.StompHeaders
import org.springframework.messaging.simp.stomp.StompSession
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter
import org.springframework.stereotype.Component

@Component
class BrokerStompSessionHandler : StompSessionHandlerAdapter() {

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

override fun afterConnected(session: StompSession, connectedHeaders: StompHeaders) {
logger.info("connected to the server: ${session.sessionId}")
session.subscribe("/user/queue/repositories-string", this)
}

override fun handleException(
session: StompSession,
command: StompCommand?,
headers: StompHeaders,
payload: ByteArray,
exception: Throwable,
) {
logger.error("handleException", exception)
logger.error(exception.message)
}

override fun handleTransportError(session: StompSession, exception: Throwable) {
logger.error("handleTransportError", exception)
logger.error(exception.message)
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package net.leanix.githubagent.runners

import net.leanix.githubagent.services.GitHubAuthenticationService
import net.leanix.githubagent.services.WebSocketService
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Component

@Component
@Profile("!test")
class PostStartupRunner(private val githubAuthenticationService: GitHubAuthenticationService) : ApplicationRunner {
class PostStartupRunner(
private val githubAuthenticationService: GitHubAuthenticationService,
private val webSocketService: WebSocketService
) : ApplicationRunner {

override fun run(args: ApplicationArguments?) {
githubAuthenticationService.generateJwtToken()
webSocketService.initSession()
}
}
25 changes: 25 additions & 0 deletions src/main/kotlin/net/leanix/githubagent/services/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.leanix.githubagent.services

import net.leanix.githubagent.client.AuthClient
import net.leanix.githubagent.config.LeanIXProperties
import org.springframework.stereotype.Service
import java.util.*

@Service
class AuthService(
private val authClient: AuthClient,
private val leanIXProperties: LeanIXProperties
) {

fun getBearerToken(): String {
return authClient.getToken(
authorization = getBasicAuthHeader(),
body = "grant_type=client_credentials",
).accessToken
}

private fun getBasicAuthHeader(): String =
"Basic " + Base64.getEncoder().encodeToString(
"apitoken:${leanIXProperties.auth.apiUserToken}".toByteArray(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.springframework.stereotype.Service

@Service
class CachingService(
private val githubEnterpriseProperties: GitHubEnterpriseProperties
private val gitHubEnterpriseProperties: GitHubEnterpriseProperties
) {

data class CacheValue(val value: Any, val expiry: Long?)
Expand Down Expand Up @@ -55,7 +55,7 @@ class CachingService(

@PostConstruct
private fun init() {
set("baseUrl", githubEnterpriseProperties.baseUrl, null)
set("githubAppId", githubEnterpriseProperties.githubAppId, null)
set("baseUrl", gitHubEnterpriseProperties.baseUrl, null)
set("githubAppId", gitHubEnterpriseProperties.gitHubAppId, null)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.leanix.githubagent.services

import net.leanix.githubagent.config.WebSocketClientConfig
import org.slf4j.LoggerFactory
import org.springframework.messaging.simp.stomp.StompSession
import org.springframework.stereotype.Service

@Service
class WebSocketService(
private val webSocketClientConfig: WebSocketClientConfig
) {

private val logger = LoggerFactory.getLogger(WebSocketService::class.java)
var stompSession: StompSession? = null

fun initSession() {
logger.info("init session")
stompSession = webSocketClientConfig.initSession()
}
}
6 changes: 6 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ github-enterprise:
baseUrl: ${GITHUB_ENTERPRISE_BASE_URL:}
githubAppId: ${GITHUB_APP_ID:}
pemFile: ${PEM_FILE:}
leanix:
base-url: https://${LEANIX_DOMAIN}/services
ws-base-url: wss://${LEANIX_DOMAIN}/services
auth:
access-token-uri: ${leanix.base-url}/mtm/v1
api-user-token: ${LEANIX_TECHNICAL_USER_TOKEN}
Loading
Loading