Spring Rest API calling service with Deffered<> - spring

my code functions, but not sure if I am implementing it properly.
I have a service layer that calls Youtrack API using Retrofit, does some post-filtering and returns a list of issues. The code below is simplified version, but should be enough to make a picture.
suspend fun getProjectIssuesFromYt(
project: String,
youTrackInstance: YouTrackInstance,
after: Int,
max: Int
): List<Issues> = coroutineScope {
val service = Youtrack.getYoutrack(youTrackInstance).service
val deferreds: Deferred<List<Issues>> =
async(Dispatchers.Default) {
service.getIssuesByProject(
project = project, max = max,
after = after
).bodyList()
}
deferreds.await()
}
How can I call this service from REST api? The only solution that is functioning is to call it with runBlocking, but I don't think this ia a way to go.
#GetMapping("/getProjectIssuesFromYt")
fun getProjectIssuesFromYt(
project: String,
youTrackInstance: YouTrackInstance,
after: Int,
max: Int
): List<Issues> = runBlocking {
clientService.getProjectIssuesFromYt(
project = project,
youTrackInstance = youTrackInstance,
after = after,
max = max
)
}
I did try making my controller function suspended and running it without runBlocking, but I am getting an error.
"Parameter specified as non-null is null: method kotlinx.coroutines.internal.ScopeCoroutine.<init>, parameter context",
Thank you in advance,
Marko

If you are using Spring MVC:
Kotlin Coroutines are only available as an alternative to Java's Project Reactor in Spring Web Flux.
Spring MVC Controllers are blocking by design that is why they cannot be implemented as suspending functions.
https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow

Related

Spring ACL with Kotlin Exposed

I'm using kotlin exposed and spring acl: JdbcMutableAclService.
The dummy code looks like:
transaction {
//operation 1
dao.updateSomething(resourceId)
val sids = dao.getUserIdsByResourceId(resourceId)
//operation 2
val pObjectIdentity = ObjectIdentityImpl(PROJECT, resourceId)
val pMutableAcl = aclService.readAclById(pObjectIdentity) as MutableAcl
var i = pMutableAcl.entries.size
sids.forEach {
pMutableAcl.insertAce(i++, BasePermission.READ, PrincipalSid(it), true)
}
aclService.updateAcl(pMutableAcl)
//operation 3
val rObjectIdentity = ObjectIdentityImpl(RESOURCE, resourceId)
val rMutableAcl = aclService.readAclById(rObjectIdentity) as MutableAcl
var i = rMutableAcl.entries.size
sids.forEach {
rMutableAcl.insertAce(i++, BasePermission.READ, PrincipalSid(it), true)
}
aclService.updateAcl(rMutableAcl)
}
If something happens in operation 3 - it won't write nothing to db, the outer transaction will also rolled back, and operation 1 won't be committed as well.
Unfortunately operation 2 won't be rolled back.
So my assumption every time of using updateAcl it creates its own isolated transaction.
I don't know how it work in case of Spring Jpa, and #Transactional annotation (is JdbcMutableAclService take into consideration outer transaction or not), but in case of Exposed it is not.
Is it correct behaviour at all? Should every acl update be an isolated transaction?
Is there a way to integrate Exposed and JdbcMutableAclService without implementing my own MutableAclService?
UPD for #Tapac
I'm using org.jetbrains.exposed:exposed-spring-boot-starter without any additional configuration, so based on ExposedAutoConfiguration it is org.jetbrains.exposed.spring.SpringTransactionManager.
But during the debugging i saw in stacktrace some refs to ThreadLocalTransactionManager.
And don't know is it useful information, but i don't use spring transaction annotation and instead of that i use exposed transaction{} block.

How to Unit test Service & controller (kotlin) in a Cordapp?

I have gone through many documentations for getting a sample of unit testing service and controller in a Cordapp, not the flows (that is already done using in corda docs). Can anyone please help me to get an example cordapp which implemented service unit testing?
Try taking a look at the CordaService Autopayroll sample on github.
link: https://github.com/corda/samples-java/tree/master/Features/cordaservice-autopayroll
There's an ability to access registered services that gets used here in the testing code
//Test #1 check if the requestState is being sent to the bank operator behind the scene.
#Test
fun `dummy test`() {
val future = a.startFlow(RequestFlowInitiator("500", b.info.legalIdentities.first()))
network.runNetwork()
val ptx = future.get()
println("Signed transaction hash: ${ptx.id}")
listOf(a, bank).map {
it.services.validatedTransactions.getTransaction(ptx.id)
}.forEach {
val txHash = (it as SignedTransaction).id
println("$txHash == ${ptx.id}")
assertEquals(ptx.id, txHash)
}
}
link: https://github.com/corda/samples-kotlin/blob/master/Features/cordaService-autopayroll/workflows-kotlin/src/test/kotlin/net/corda/examples/autopayroll/FlowTests.kt
Good luck!
We can use Mockito module for mocking and stabbing that is required for unit testing service functions and APIs.
This link will direct more on how to mock CordaRPCops using mockito as an example.

Spring Kotlin Webapp without Webflux need to perform 2 tasks concurrently to yield a result best way

The app does not use webflux and reactive programming, it use a normal CrudRepository to connect to a database that some time takes long time to respond, and it use the WebClient to perform requests to other services but using block() function to get the result in a synchronous way. I want to change the following code so both calls happen concurrently:
#Service class CustomerService(
val profileClient: WebClient,
val customerRepository: CustomerRepository
) {
fun getCustomer(id: String) : CustomerData {
val customer = customerRepository.findById(id)
val profile = profileClient.get().uri("/v1/profile/{id}", id)
.retrieve().bodyToMono<Profile>()
.block()
return CustomerData(customer, profile)
}
}
If the call to customerRepository.findById(id) takes lets say 20 millis, and the profileClient.get.. takes 50 millis, the overall takes 70 millis, while if I call both calls concurrently, it should take around 50 millis.
I cannot migrate the app to a fully reactive version with Webflux due it has a lot of code to migrate.
If you need concurrency, you may use kotlin's coroutines.
Your code would look like this:
fun getCustomer(id: String) : CustomerData = runBlocking {
val customer = async { customerRepository.findById(id) }
val profile = async { profileClient.get().uri("/v1/profile/{id}", id)
.retrieve().bodyToMono<Profile>()
.block() }
CustomerData(customer.await(), profile.await())
}

Using ReactiveSecurityContextHolder inside a Kotlin Flow

I'm working on a Spring Boot (2.2) project using Kotlin, with CouchDB as (reactive) database, and in consequence, async DAO (either suspend functions, or functions returning a Flow). I'm trying to setup WebFlux in order to have async controllers too (again, I want to return Flows, not Flux). But I'm having troubles retrieving my security context from ReactiveSecurityContextHolder.
From what I've read, unlike SecurityContextHolder which is using ThreadLocal to store it, ReactiveSecurityContextHolder relies on the fact that Spring, while making a subscription to my reactive chain, also stored that context inside this chain, thus allowing me to call ReactiveSecurityContextHolder.getContext() from within the chain.
The problem is that I have to transform my Mono<SecurityContext> into a Flow at some point, which makes me loose my SecurityContext. So my question is: is there a way to have a Spring Boot controller returning a Flow while retrieving the security context from ReactiveSecurityContextHolder inside my logic? Basically, after simplification, it should look like this:
#GetMapping
fun getArticles(): Flow<String> {
return ReactiveSecurityContextHolder.getContext().flux().asFlow() // returns nothing
}
Note that if I return the Flux directly (skipping the .asFlow()), or add a .single() or .toList() in the end (hence using a suspend fun), then it works fine and my security context is returned, but again that's not what I want. I guess the solution would be to transfer the context from the Flux (initial reactive chain from ReactiveSecurityContextHolder) to the Flow, but it doesn't seem to be done by default.
Edit: here is a sample project showcasing the problem: https://github.com/Simon3/webflux-kotlin-sample
What you really try to achieve is accessing your ReactorContext from inside a Flow.
One way to do this is to relax the need for returning a Flow and return a Flux instead. This allows you to recover the ReactorContext and pass it to the Flow you are going to use to generate your data.
#ExperimentalCoroutinesApi
#GetMapping("/flow")
fun flow(): Flux<Map<String, String>> = Mono.subscriberContext().flatMapMany { reactorCtx ->
flow {
val ctx = coroutineContext[ReactorContext.Key]?.context?.get<Mono<SecurityContext>>(SecurityContext::class.java)?.asFlow()?.single()
emit(mapOf("user" to ((ctx?.authentication?.principal as? User)?.username ?: "<NONE>")))
}.flowOn(reactorCtx.asCoroutineContext()).asFlux()
}
In the case when you need to access the ReactorContext from a suspend method, you can simply get it back from the coroutineContext with no further artifice:
#ExperimentalCoroutinesApi
#GetMapping("/suspend")
suspend fun suspend(): Map<String,String> {
val ctx = coroutineContext[ReactorContext.Key]?.context?.get<Mono<SecurityContext>>(SecurityContext::class.java)?.asFlow()?.single()
return mapOf("user" to ((ctx?.authentication?.principal as? User)?.username ?: "<NONE>"))
}

The best way to repeat the request in Project reactor

I am quite new in reactive programming.
We have an application in SpringBoot using project-reactor. Inside this, we make an HTTP request to a third party service and get a Mono as a result.
fun getResultFromService() : Mono<Result> {
//requesting the third party REST API
}
I would like to:
check the response code
for some values repeat a request M times with N seconds difference between them
What is the best way to do it?
what should I use instead of Thread.sleep()
I checked repeatWhenEmpty, I don't think it is suitable only for M tries.
The key is that you need to convert the error response code into an error on Mono. After doing this you can use the retryBackoff operator mentioned by Michael Berry in the comments.
fun main()
{
getResultFromService()
.flatMap {
if (it.statusCode == 500 )
Mono.error(RuntimeException("Error which should be retried"))
else Mono.just(it)
}.retryBackoff(3, Duration.ofMillis(500), Duration.ofMillis(500))
.block()
}
fun getResultFromService() : Mono<Result>
{
//requesting the third party REST API
TODO("Implement it.")
}
data class Result(
val statusCode: Int,
val response: Any
)

Resources