Update XsClient functionality for stress testing
parent
86f3f66d50
commit
e2c13c2503
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
@ -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() }
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue