-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
281 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
group=io.wafflestudio.truffle.sdk | ||
group=com.wafflestudio.truffle.sdk | ||
version=1.0.0-SNAPSHOT | ||
kotlin.code.style=official |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
dependencies { | ||
compileOnly("org.springframework.boot:spring-boot-starter-webflux") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.wafflestudio.truffle.sdk.core | ||
|
||
import com.wafflestudio.truffle.sdk.core.protocol.TruffleApp | ||
import com.wafflestudio.truffle.sdk.core.protocol.TruffleEvent | ||
import com.wafflestudio.truffle.sdk.core.protocol.TruffleException | ||
import com.wafflestudio.truffle.sdk.core.protocol.TruffleRuntime | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.SupervisorJob | ||
import kotlinx.coroutines.asCoroutineDispatcher | ||
import kotlinx.coroutines.flow.MutableSharedFlow | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.reactor.awaitSingle | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.web.reactive.function.client.WebClient | ||
import org.springframework.web.reactive.function.client.bodyToMono | ||
import java.time.Duration | ||
import java.util.concurrent.Executors | ||
|
||
interface TruffleClient { | ||
fun sendEvent(ex: Throwable) | ||
} | ||
|
||
class DefaultTruffleClient( | ||
name: String, | ||
phase: String, | ||
apiKey: String, | ||
webClientBuilder: WebClient.Builder, | ||
) : TruffleClient { | ||
private val events = MutableSharedFlow<TruffleEvent>(extraBufferCapacity = 10) | ||
|
||
private val logger = LoggerFactory.getLogger(javaClass) | ||
|
||
private val truffleApp = TruffleApp(name, phase) | ||
private val truffleRuntime = TruffleRuntime(name = "Java", version = System.getProperty("java.version")) | ||
|
||
init { | ||
val coroutineScope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) | ||
val webClient = webClientBuilder | ||
.baseUrl("https://truffle-api.wafflestudio.com") | ||
.defaultHeader("x-api-key", apiKey) | ||
.build() | ||
|
||
coroutineScope.launch(SupervisorJob()) { | ||
events.collect { | ||
runCatching { | ||
webClient | ||
.post() | ||
.uri("/events") | ||
.bodyValue(it) | ||
.retrieve() | ||
.bodyToMono<Unit>() | ||
.timeout(Duration.ofSeconds(1)) | ||
.awaitSingle() | ||
}.getOrElse { | ||
logger.warn("Failed to request to truffle server", it) | ||
} | ||
} | ||
} | ||
} | ||
|
||
override fun sendEvent(ex: Throwable) { | ||
events.tryEmit( | ||
TruffleEvent( | ||
app = truffleApp, | ||
runtime = truffleRuntime, | ||
exception = TruffleException(ex), | ||
) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.wafflestudio.truffle.sdk.core.protocol | ||
|
||
data class TruffleApp( | ||
val name: String, | ||
val phase: String? = null, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.wafflestudio.truffle.sdk.core.protocol | ||
|
||
data class TruffleEvent( | ||
val version: String = TruffleVersion.V1, | ||
val app: TruffleApp, | ||
val runtime: TruffleRuntime, | ||
val exception: TruffleException, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.wafflestudio.truffle.sdk.core.protocol | ||
|
||
data class TruffleException( | ||
val className: String, | ||
val message: String?, | ||
val elements: List<Element>, | ||
) { | ||
data class Element( | ||
val className: String, | ||
val methodName: String, | ||
val lineNumber: Int, | ||
val fileName: String, | ||
val isInAppInclude: Boolean, | ||
) | ||
} | ||
|
||
fun TruffleException(e: Throwable): TruffleException = TruffleException( | ||
className = e.javaClass.name, | ||
message = e.message, | ||
elements = e.stackTrace.map { | ||
TruffleException.Element( | ||
className = it.className, | ||
methodName = it.methodName, | ||
lineNumber = it.lineNumber, | ||
fileName = it.fileName ?: "", | ||
isInAppInclude = true, // FIXME | ||
) | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.wafflestudio.truffle.sdk.core.protocol | ||
|
||
data class TruffleRuntime( | ||
val name: String, | ||
val version: String, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.wafflestudio.truffle.sdk.core.protocol | ||
|
||
interface TruffleVersion { | ||
companion object { | ||
const val V1 = "v1" | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
truffle-spring-boot-starter/src/main/kotlin/TruffleAutoConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.wafflestudio.truffle.sdk | ||
|
||
import com.wafflestudio.truffle.sdk.core.DefaultTruffleClient | ||
import com.wafflestudio.truffle.sdk.core.TruffleClient | ||
import com.wafflestudio.truffle.sdk.reactive.TruffleWebExceptionHandler | ||
import com.wafflestudio.truffle.sdk.servlet.TruffleHandlerExceptionResolver | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.web.reactive.function.client.WebClient | ||
import org.springframework.web.server.WebExceptionHandler | ||
import org.springframework.web.servlet.HandlerExceptionResolver | ||
|
||
@EnableConfigurationProperties(TruffleProperties::class) | ||
@Configuration | ||
class TruffleAutoConfiguration { | ||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) | ||
@Configuration | ||
class TruffleServletConfiguration { | ||
@Bean | ||
fun truffleHandlerExceptionResolver(truffleClient: TruffleClient): HandlerExceptionResolver { | ||
return TruffleHandlerExceptionResolver(truffleClient) | ||
} | ||
} | ||
|
||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) | ||
@Configuration | ||
class TruffleReactiveConfiguration { | ||
@Bean | ||
fun truffleWebExceptionHandler(truffleClient: TruffleClient): WebExceptionHandler { | ||
return TruffleWebExceptionHandler(truffleClient) | ||
} | ||
} | ||
|
||
@Bean | ||
fun truffleClient(properties: TruffleProperties, webClientBuilder: WebClient.Builder): TruffleClient = | ||
DefaultTruffleClient( | ||
name = properties.name, | ||
phase = properties.phase, | ||
apiKey = properties.apiKey, | ||
webClientBuilder = webClientBuilder, | ||
) | ||
} |
10 changes: 10 additions & 0 deletions
10
truffle-spring-boot-starter/src/main/kotlin/TruffleProperties.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.wafflestudio.truffle.sdk | ||
|
||
import org.springframework.boot.context.properties.ConfigurationProperties | ||
|
||
@ConfigurationProperties("truffle.client") | ||
data class TruffleProperties( | ||
val name: String, | ||
val phase: String, | ||
val apiKey: String, | ||
) |
7 changes: 0 additions & 7 deletions
7
truffle-spring-boot-starter/src/main/kotlin/config/TruffleAutoConfiguration.kt
This file was deleted.
Oops, something went wrong.
21 changes: 21 additions & 0 deletions
21
truffle-spring-boot-starter/src/main/kotlin/reactive/TruffleWebExceptionHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.wafflestudio.truffle.sdk.reactive | ||
|
||
import com.wafflestudio.truffle.sdk.core.TruffleClient | ||
import org.springframework.core.annotation.Order | ||
import org.springframework.web.server.ResponseStatusException | ||
import org.springframework.web.server.ServerWebExchange | ||
import org.springframework.web.server.WebExceptionHandler | ||
import reactor.core.publisher.Mono | ||
|
||
@Order(-2) | ||
class TruffleWebExceptionHandler( | ||
private val truffleClient: TruffleClient, | ||
) : WebExceptionHandler { | ||
override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> { | ||
if (ex !is ResponseStatusException) { | ||
truffleClient.sendEvent(ex) | ||
} | ||
|
||
return Mono.error(ex) | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
truffle-spring-boot-starter/src/main/kotlin/servlet/TruffleHandlerExceptionResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.wafflestudio.truffle.sdk.servlet | ||
|
||
import com.wafflestudio.truffle.sdk.core.TruffleClient | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.core.annotation.Order | ||
import org.springframework.web.server.ResponseStatusException | ||
import org.springframework.web.servlet.HandlerExceptionResolver | ||
import org.springframework.web.servlet.ModelAndView | ||
import java.lang.Exception | ||
|
||
@Order(-2) | ||
class TruffleHandlerExceptionResolver( | ||
private val truffleClient: TruffleClient, | ||
) : HandlerExceptionResolver { | ||
override fun resolveException( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
handler: Any?, | ||
ex: Exception, | ||
): ModelAndView? { | ||
if (ex !is ResponseStatusException) { | ||
truffleClient.sendEvent(ex) | ||
} | ||
|
||
return null | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
truffle-spring-boot-starter/src/main/resources/META-INF/spring.factories
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | ||
io.wafflestudio.truffle.sdk.config.TruffleAutoConfiguration | ||
com.wafflestudio.truffle.sdk.TruffleAutoConfiguration |
38 changes: 38 additions & 0 deletions
38
truffle-spring-boot-starter/src/test/kotlin/TestTruffleApplication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.wafflestudio.truffle.sdk | ||
|
||
import org.junit.jupiter.api.Test | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.autoconfigure.SpringBootApplication | ||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.test.web.reactive.server.WebTestClient | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@SpringBootApplication | ||
class TestTruffleApplication { | ||
@RestController | ||
class TestController { | ||
@GetMapping("/test") | ||
fun test(): String { | ||
throw RuntimeException("test") | ||
} | ||
} | ||
} | ||
|
||
@AutoConfigureWebTestClient | ||
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"]) | ||
class TruffleReactiveTest( | ||
@Autowired private val webTestClient: WebTestClient, | ||
) { | ||
@Test | ||
fun truffleTest() { | ||
webTestClient.get() | ||
.uri("/test") | ||
.exchange() | ||
.expectStatus() | ||
.is5xxServerError | ||
|
||
Thread.sleep(3000) | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
truffle-spring-boot-starter/src/test/resources/application.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
truffle: | ||
client: | ||
name: snutt | ||
phase: dev | ||
api-key: abcdef |