How to use kotlin coroutines with reactive spring data - spring

I am trying to migrate some project from Spring Reactor to kotlin coroutines. I have some controller based on spring webflux like that:
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
fun find(#RequestParam id: String): Mono<Product> {
return productRepository.findById(id)
}
}
This controller uses reactive spring data repository:
#Repository
interface ProductsRepository : ReactiveMongoRepository<Product, String>
According to this official documentation - https://docs.spring.io/spring/docs/5.2.0.M1/spring-framework-reference/languages.html#how-reactive-translates-to-coroutines, my function find in controller should be translated to suspend fun and this function should return an instance of Product class instead of reactive Mono wrapper of Product. Something like that:
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
suspend fun find(#RequestParam id: String): Product {
return productRepository.findById(id)
}
}
But my productRepository deals with Mono and Flux, not suspended functions. How should I use spring data abstraction properly in that case?

This can be achieved with the useful kotlinx-coroutines-reactor helper library which provides useful extensions methods for project reactors Publisher to help between converting Mono or Flux to kotlin coroutines.
First add a dependency on
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
</dependency>
(if your using spring-boot you do not have to specify a version as it manages it for you)
You can now use kotlinx.coroutines.reactive.awaitFirstOrNull to convert a Mono<Product> to Product? and 'await' the result.
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
suspend fun find(#RequestParam id: String): Product? {
return productRepository.findById(id).awaitFirstOrNull()
}
}

Related

Additional methods to the generated Panache REST Data resource using repository pattern

I'm using Quarkus with Hibernate Reactive and Panache REST Data. I'm using the Repository pattern. I would like to use the PanacheRepositoryResource and add a custom endpoint that calls a method in the EntityRepository, but I don't know how to inject the entityRepository since it is an interface.
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return entityRepository.customMethod(); // <-- How can I inject my repository?
}
}
Any ideas?
You can do something like this:
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return CDI.current().select(EntityRepository.class).get().customMethod();
}
}

Custom ConditionalGenericConverter annotation doesn't convert parameters in Spring GraphQL

I have a REST API developed in Spring and now I'm learning how to use GraphQL in my Spring project so I'm "translating" my REST endpoints to GraphQL queries.
Just to give some background on what I'm trying to do, I will show how one of my REST controllers look like (code in Kotlin):
Rest Controller
#RestController
#RequestMapping("countries")
class CountryController(private val countryService: CountryService) {
#GetMapping("{code}")
fun findByCode(#PathVariable #Uppercase code: String): Country {
return countryService.findByCode(code)
}
}
It's basically an endpoint that fetches a country based on a code. For example /countries/bra will fetch information about the country Brazil.
In the code above there's also a custom annotation called #Uppercase. This annotation extends ConditionalGenericConverter and the only thing it does is to take the code parameter and convert it to uppercase. This is the code for this annotation:
#Uppercase implementation
#Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Uppercase
#Component
class UppercaseConverter : ConditionalGenericConverter {
override fun matches(sourceType: TypeDescriptor, targetType: TypeDescriptor): Boolean {
return targetType.getAnnotation(Uppercase::class.java) != null
}
override fun getConvertibleTypes(): MutableSet<ConvertiblePair>? {
return mutableSetOf(ConvertiblePair(String::class.java, String::class.java))
}
override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): String {
return (source as String).uppercase()
}
}
Everything works as expected in the code above and the code parameter is always converted to uppercase if I use the #Uppercase annotation in my REST controller.
Now I created a similar controller for Spring GraphQL that looks like this:
GraphQL Controller
#Controller
class CountryResolver(private val countryService: CountryService) {
#QueryMapping(name = "country")
fun findByCode(#Argument #Uppercase code: String): Country {
return countryService.findByCode(code)
}
}
And my GraphQL is executed properly with the code above, except that the parameter code is not converted to uppercase when I use my custom annotation #Uppercase. It seems that my custom annotation only works with #PathVariable in the REST controller, but it doesn't with #Argument in the GraphQL controller.
Any idea why on how to make it work with GraphQL as well? Thanks in advance!

Does Spring-Boot handle Kotlin coroutines apart from WebFlux context?

We are trying to use Kotlin coroutines for asynchronous processing inside Spring-Boot backend.
The problem is that it doesn't seem to support it well (At least standard Spring MVC).
Basically, if we have a function that does asynchronous logic:
fun fetchUsersAsync(): Deferred<Users> {
return GlobalScope.async {
...
}
}
and this function is used with await at some point in service, which requires to put suspend annotation in a calling service function:
#Service
class MyService {
suspend fun processUsers(): Users {
return fetchUsersAsync().await()
}
}
Unfortunately it is not possible, and the only reference for suspend functionality in service was connected with WebFlux.
Has anyone faced the same situation? Thanks.
If you want to call await() without declaring a suspend function, wrap it inside a coroutine builder, like this:
#Service
class MyService {
fun processUsers(): Users {
return runBlocking { fetchUsersAsync().await() }
}
}

Spring + Kotlin - Is it possible to detect certain functions calls?

I've just started using Spring and Kotlin and wanted to ask is it possible for my app to detect certain function calls?
For example, say I have:
fun getSuccessMessage() : String {
return "great"
}
Then in my app runner I call that:
#Component
class AppRunner: CommandLineRunner {
#Throws(Exception::class)
override fun run(vararg args: String) {
getSuccessMessage()
}
}
Can I have another function thats listening and acts whenever its called:
fun doSomethingWhenSuccessCalled() {
// I'm imaging some magic Spring annotation where I can say
//something like #ListeningTo("getSuccessMessage")
}
No, it is not possible.
To read more about listener of method invoke look at:
Listener on Method.invoke java

Updating a hibernate entity with CrudRepository

I'm currently writing my first spring boot Kotlin application and am trying to create a rest API with JPA persistence. The basics are going fine but I'm struggling with updating a model on a patch endpoint (#patchMapping).
I want to adhere to proper rest standards and for that reason I'm hitting the patch endpoint with #PatchMapping("/company/{id}").
I would like to be able to call the CrudRepository in a way like this.
#PatchMapping("/company/{id}")
fun update(#PathVariable id: Long, #RequestBody updateRequest: Company) : Company {
return repository.update(updateRequest, id)
}
but it appears as if the spring way to do it is to pass the id of the object you're going to update within the requestBody? e.g.
repository.save(updateRequest)
which then auto merges the object. But this conflicts with any sane rest convention...
is there an integrated solution available for what I want to achieve? I'd like to refrain from writing my own logic as I'd hoped spring to have this functionality.
Do you need something like this?
#RestController
class Controller(private val service: CompanyService) {
#PatchMapping("/company/{id}")
fun update(#PathVariable id: Long, #RequestBody company: Company): Company {
return service.updateCompany(company, id)
}
}
#Service
class CompanyService (private val repository: CompanyRepository) {
#Transactional
fun updateCompany(company: Company, id: Long): Company {
val companyToUpdate = repository.findOne(id)
companyToUpdate.setSomething(company.getSomething)
raturn companyToUpdate;
}
}
interface CompanyRepository : CrudRepository<Company, Long>

Resources