Update XsClient functionality for stress testing

16
Timo Bryant 2024-01-12 17:10:02 +01:00
parent 86f3f66d50
commit e2c13c2503
5 changed files with 72 additions and 27 deletions

View File

@ -12,6 +12,7 @@ import korlibs.korge.input.*
import korlibs.korge.tween.get import korlibs.korge.tween.get
import korlibs.korge.tween.tween import korlibs.korge.tween.tween
import korlibs.korge.ui.tooltip import korlibs.korge.ui.tooltip
import korlibs.korge.ui.uiCheckBox
import korlibs.logger.AnsiEscape import korlibs.logger.AnsiEscape
import korlibs.math.geom.* import korlibs.math.geom.*
import korlibs.math.geom.shape.toShape2D import korlibs.math.geom.shape.toShape2D
@ -89,6 +90,8 @@ class ViewDocument(private val document: Document) : Scene() {
val imageFile = localCurrentDirVfs["assets/xs-reg/00001.jpg"].readBitmap() val imageFile = localCurrentDirVfs["assets/xs-reg/00001.jpg"].readBitmap()
image(imageFile) image(imageFile)
uiCheckBox { text = "foo" }
document.retrieveOcrPages().first().words.forEach { word -> document.retrieveOcrPages().first().words.forEach { word ->
solidRect( solidRect(
width = word.rectangle.width, width = word.rectangle.width,

View File

@ -4,6 +4,7 @@ import com.github.ajalt.clikt.parameters.options.convert
import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.int import com.github.ajalt.clikt.parameters.types.int
import de.itkl.httpClient.clients.TaskWaitStatus
import de.itkl.httpClient.clients.XsClient import de.itkl.httpClient.clients.XsClient
import de.itkl.httpClient.httpClientModule import de.itkl.httpClient.httpClientModule
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -15,6 +16,7 @@ import org.koin.core.context.GlobalContext.startKoin
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.time.Duration.Companion.minutes
class Cli : CliktCommand() { class Cli : CliktCommand() {
override fun run() {} override fun run() {}
@ -36,10 +38,14 @@ class XsCli : KoinComponent {
private val xsClient: XsClient by inject() private val xsClient: XsClient by inject()
suspend fun run(tasks: Int, inputDirectory: Path) = coroutineScope { suspend fun run(tasks: Int, inputDirectory: Path) = coroutineScope {
val files = inputDirectory.toFile().listFiles()!!.toList().filter { it.isFile }.filter { it.extension in listOf("pdf", "ttf", "jpg", "jpeg") } val files = inputDirectory.toFile()
.listFiles()!!
.toList()
.filter { it.isFile }.filter { it.extension in listOf("pdf", "ttf", "jpg", "jpeg") }
.filter { !it.name.startsWith(".") }
val random = Random() val random = Random()
(0..tasks) val (success, error) = (0..<tasks)
.map { .map {
val file = files[random.nextInt(files.size)] val file = files[random.nextInt(files.size)]
async { async {
@ -48,6 +54,9 @@ class XsCli : KoinComponent {
} }
} }
.awaitAll() .awaitAll()
.partition { it.status == TaskWaitStatus.SUCCESS }
println("Summary: ${success.size + error.size}: ${success.size}/${error.size}")
} }
} }
@ -55,6 +64,5 @@ suspend fun main(args: Array<String>) {
startKoin { startKoin {
modules(httpClientModule) modules(httpClientModule)
} }
XsCli().run(tasks = 1, inputDirectory = Paths.get("assets/xs-reg")) XsCli().run(tasks = 100, inputDirectory = Paths.get("assets/xs-reg"))
// Cli().main(args)
} }

View File

@ -29,16 +29,27 @@ private val Log = KotlinLogging.logger { }
class XsClient : KoinComponent { class XsClient : KoinComponent {
private val httpClient by inject<HttpClient>() private val httpClient by inject<HttpClient>()
suspend fun waitFor(task: XsTask): WaitForResponse {
suspend fun waitFor(task: XsTask) {
Log.info { "Wait for competition: $task" } Log.info { "Wait for competition: $task" }
val response = httpClient.get { val response = httpClient.get {
url("http://localhost:8080/api/v1/analyse/tasks/wait/${task.xsTaskId.taskId}") url("http://localhost:8080/api/v1/analyse/tasks/wait/${task.xsTaskId.taskId}")
user = "xs.dev.klara" user = "xs.dev.klara"
} }
val responseText = response.bodyAsText() val result = response.body<WaitForResponse>()
Log.info { "Waiting done for task $task: ${response.status}: $responseText" } Log.info { "Waiting done for task $task: ${response.status}: $result" }
return result
}
suspend fun analyseResult(task: XsTask) {
val response = httpClient.get {
url("http://localhost:8080/api/v1/analyse-async-result/${task.xsTaskId.taskId}")
user = "xs.dev.klara"
}
check(response.status.isSuccess()) {
"HTTP Error"
}
val text = response.bodyAsText()
println(text)
} }
suspend fun analyse(image: Path): TaskReference { suspend fun analyse(image: Path): TaskReference {
Log.info { "Starting analysis for image at path: $image" } Log.info { "Starting analysis for image at path: $image" }
@ -105,6 +116,18 @@ data class XsTaskId(val tenantId: String, val taskId: String) {
} }
} }
@Serializable
data class WaitForResponse(
val xsTaskId: XsTaskId,
val status: TaskWaitStatus,
)
@Serializable
enum class TaskWaitStatus {
ERROR,
SUCCESS
}
fun Path.toChannelProvider(): ChannelProvider { fun Path.toChannelProvider(): ChannelProvider {
val file = toFile() val file = toFile()
return ChannelProvider(file.length()) { file.readChannel() } return ChannelProvider(file.length()) { file.readChannel() }

View File

@ -16,7 +16,7 @@ fun createHttpClient(): HttpClient {
} }
install(smartCloudAuthPlugin) install(smartCloudAuthPlugin)
install(HttpTimeout) { install(HttpTimeout) {
requestTimeoutMillis = 1.minutes.inWholeMilliseconds requestTimeoutMillis = 30.minutes.inWholeMilliseconds
} }
engine { engine {

View File

@ -3,6 +3,7 @@ package de.itkl.httpClient.implementation
import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Cache
import de.itkl.httpClient.auth.* import de.itkl.httpClient.auth.*
import de.itkl.httpClient.clients.user
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*
@ -13,12 +14,16 @@ import io.ktor.util.*
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
private val Log = KotlinLogging.logger { } private val Log = KotlinLogging.logger { }
class SmartCloudAuthStrategy : AuthStrategy, KoinComponent { class SmartCloudAuthStrategy : AuthStrategy, KoinComponent {
private val credentialsProvider: CredentialsProvider by inject() private val credentialsProvider: CredentialsProvider by inject()
private val loginMutex = Mutex()
// Cache Helpers // Cache Helpers
private val tokenCache: Cache<String, AuthenticationToken> = Caffeine.newBuilder() private val tokenCache: Cache<String, AuthenticationToken> = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS) .expireAfterWrite(1, TimeUnit.HOURS)
@ -31,31 +36,37 @@ class SmartCloudAuthStrategy : AuthStrategy, KoinComponent {
return null return null
} }
// Return cached token if present
tokenCache.getIfPresent(user)?.let { tokenCache.getIfPresent(user)?.let {
Log.info { "Returning cached token for user: $user" } Log.info { "Returning cached token for user: $user" }
return it return it
} }
val credentials: Credentials = credentialsProvider.lookupByUsername(user) ?: error("No credentials found for user: $user") return loginMutex.withLock {
val loginAndPassword: Credentials.LoginAndPassword = credentials as? Credentials.LoginAndPassword ?: error("Only username and password is supported by smartcloud auth login") tokenCache.getIfPresent(user)?.let {
Log.info { "Returning cached token for user: $user" }
return it
}
Log.debug { "User: $user, using smartcloud auth login" } val credentials: Credentials = credentialsProvider.lookupByUsername(user) ?: error("No credentials found for user: $user")
val loginAndPassword: Credentials.LoginAndPassword = credentials as? Credentials.LoginAndPassword ?: error("Only username and password is supported by smartcloud auth login")
val response = httpClient.post { Log.debug { "User: $user, using smartcloud auth login" }
url("https://api.internal.insiders.cloud/1/rest/accounts/authentication/requesttoken")
contentType(ContentType.Application.Json) val response = httpClient.post {
setBody(loginAndPassword) url("https://api.internal.insiders.cloud/1/rest/accounts/authentication/requesttoken")
contentType(ContentType.Application.Json)
setBody(loginAndPassword)
}
check(response.status.isSuccess()) {
"could not login into smart cloud: ${response.bodyAsText()}"
}
val token = response.body<BearerToken>()
Log.info { "Login successful. Valid until: ${token.validUntil}" }
// Cache the token after successful login
tokenCache.put(user, token)
token
} }
check(response.status.isSuccess()) {
"could not login into smart cloud: ${response.bodyAsText()}"
}
val token = response.body<BearerToken>()
Log.info { "Login successful. Valid until: ${token.validUntil}" }
// Cache the token after successful login
tokenCache.put(user, token)
return token
} }
} }