rxjava using Single to pass a parameter in the doOnSuccess - java

Java 8
io.reactivex:rxjava:1.3.6
kotlin 1.2.21
I am trying to pass a parameter in the doOnSuccess using a Single.
However, I am getting an error, that I can't seem to solve.
class TranslateInteractor(val repository: Respository) {
fun requestNetworkTranslated(message: String): Completable {
return repository.fetchTranslation(message)
.doOnSuccess { handleTranslationResponse(message, it) }.toCompletable()
}
private fun handleTranslationResponse(message, String, translatedResponse: TranslatedResponse) {
saveTranslation(
"",
translatedResponse.content,
translatedResponse.translationEngine)
}
private fun saveTranslation(message: String, content: String, engine: String) {
/* Save to local storage */
}
}
The error is in the .doOnSuccess. As I want to pass the message to the handleTranslationResponse.
Type mismatch: inferred type is String but TranslatedResponse was expected
How can I pass the message and what I get from the TranslatedResponse back to the HandleTranslationResponse?
The repository which is a java class that returns a single:
public class Respository {
private NetworkAPI networkAPI;
public Single<TranslatedResponse> fetchTranslation(final String message) {
return networkAPI.fetchTranslation(new TranslateRequest(message));
}
}
My Data classes in Kotlin are:
data class TranslatedResponse(
val content: String,
val translationEngine: String)
And request
data class TranslateRequest(val message: String)
For the networkAPI I have the following in java snippet:
public interface NetworkAPI {
Single<TranslatedResponse> fetchTranslation(TranslateRequest request);
}
Many thanks for any suggestions,

The reason you can't do that is because doOnSucess takes only one parameter, which is whatever the Repository returns.
You'll need to somehow add your original message to the response that the Repository returns. There are a couple of options (e.g. using a Pair or a new data class)
An example:
data class Translation(
val originalMessage: String,
val translatedMessage: TranslationResponse
)
Now your Repository becomes:
public class Respository {
private NetworkAPI networkAPI;
public Single<TranslatedResponse> fetchTranslation(final String message) {
return new Translation(
message,
networkAPI.fetchTranslation(new TranslateRequest(message)
);
}
}
Finally, handleTranslationResponse now takes a Translation instead, which makes your interactor look like:
class TranslateInteractor(val repository: Respository) {
fun requestNetworkTranslated(message: String): Completable {
return repository.fetchTranslation(message)
.doOnSuccess { handleTranslationResponse(it) }.toCompletable()
}
private fun handleTranslationResponse(translation: Translation) {
saveTranslation(
translation.originalMessage,
translation.translatedMessage.content,
translation.translatedMessage.translationEngine)
}
private fun saveTranslation(message: String, content: String, engine: String) {
// do whatever you want!
}
}

Related

Hilla Type 'Promise<void>' is missing the following properties from type

Trying to create an application in hilla(vaadin), which will list product and their details. For categorizing the product need to store the information related to category, but the issue facing while trying to list the category in a hilla/vaadin grid. From the endpoint returning a list of category and in the store consumed the list and try to display in the grid. While trying to run the application getting th error as "
Type 'Promise<void>' is missing the following properties from type
"
#Nonnull is specified in response of the list and in method still getting error
Below is the endpoint class
#Endpoint
#AnonymousAllowed
public class ProductEndpoint {
private InterProductService productService;
#Autowired
public ProductEndpoint(InterProductService productService) {
this.productService = productService;
}
public List<ProductDataList> fetchAllProducts() {
return this.productService.fetchProducts("");
}
public #Nonnull List<#Nonnull ProductCategoryDataList> fetchAllProdCategory() {
return this.productService.fetchProductCategory("");
}
#Nonnull
public EndpointResponse saveCatgeory(#Nonnull CategoryDetails categoryDetails) {
return this.productService.saveCategory(categoryDetails);
}
}
Below is the store
export class ProductStore {
constructor() {
makeAutoObservable(
this);
this.fetchProductCatgeory();
}
async saveProductCategory(prodCategory: CategoryDetails) {
const responseData = await ProductEndpoint.saveCatgeory(prodCategory);
return responseData;
}
async fetchProductCatgeory() {
const prodCatData = await ProductEndpoint.fetchAllProdCategory();
runInAction(() => {
return prodCatData;
});
}
}
Store class specific to that module of product and catgeory
export class CategoryListRegisterViewStore {
categoryList: ProductCategoryDataList[] = [];
constructor() {
makeAutoObservable(
this,
{
categoryList: observable.shallow,
}
);
this.loadProductCategory();
}
loadProductCategory() {
//return appStore.tricampCrmProductStore.fetchProductCatgeory();
const prodCategory = appStore.tricampCrmProductStore.fetchProductCatgeory();
runInAction(() => {
this.categoryList = prodCategory;
});
}
saveProductCategory(categoryDetails: CategoryDetails) {
return appStore.tricampCrmProductStore.saveProductCategory(categoryDetails);
}
}
export const categoryListRegisterViewStore = new CategoryListRegisterViewStore();
Html page with grid code is below
<vaadin-grid theme="row-stripes" .items="${categoryListRegisterViewStore.loadProductCategory}" >
<vaadin-grid-column header="Action" frozen-to-end auto-width flex-grow="0" ${columnBodyRenderer(this.actionRenderer,
[])}>
</vaadin-grid-column>
Tried multiple methods like returning from the method directly but getting different error like AllItem is not iterate. While inspect the promise also came "undefined". So maybe i did some mistake, expecting someone can support to fix the issue
There are multiple issues in your code:
The grid's items property is bound to the method for loading the categories. You should bind it to the categories loaded from that method, which should be stored in categoryListRegisterViewStore.categoryList:
<vaadin-grid theme="row-stripes" .items="${categoryListRegisterViewStore.categoryList}" >
The CategoryListRegisterViewStore.loadProductCategory method tries to assign the result of fetchProductCatgeory, which returns a Promise, to the categoryList property, which is of type ProductCategoryDataList[]. That's where you're getting the type error from. You should wait for the promise to resolve and then store the result of the promise:
async loadProductCategory() {
const prodCategory = await appStore.tricampCrmProductStore.fetchProductCatgeory();
runInAction(() => {
this.categoryList = prodCategory;
});
}
Finally, the fetchProductCatgeory method is flawed. It doesn't return the result of the endpoint call, because you wrapped the return into a runInAction. Instead, just directly return the promise from the endpoint call:
fetchProductCatgeory() {
return ProductEndpoint.fetchAllProdCategory();
}
At this point you might consider removing that method entirely and directly using the endpoint in loadProductCategory instead.

Vert.x 4 eventbus serialize multiple classes with same codec

Is there a way to register a codec for multiple classes? Basically, all my classes should just be serialized using a Jackson object mapper. But it seems like I have to create a custom codec for each class (even though I can abstract it a little bit using generics).
A small code example:
Codec:
class JacksonCodec<T>(private val mapper: ObjectMapper, private val clazz: Class<T>) : MessageCodec<T, T> {
override fun encodeToWire(buffer: Buffer, s: T) {
buffer.appendBytes(mapper.writeValueAsBytes(s))
}
override fun decodeFromWire(pos: Int, buffer: Buffer): T {
val length = buffer.getInt(pos)
val bytes = buffer.getBytes(pos + 4, pos + 4 + length)
return mapper.readValue(bytes, clazz)
}
...
}
register codec for each class I want to serialize:
vertx.eventBus()
.registerDefaultCodec(A::class.java, JacksonCodec(DatabindCodec.mapper(), A::class.java))
vertx.eventBus()
vertx.eventBus()
.registerDefaultCodec(B::class.java, JacksonCodec(DatabindCodec.mapper(), B::class.java))
vertx.eventBus()
The code examples are kotlin but same applies for Java.
As far as I can tell looking at the code, there is no way, as the class needs to be the exact match:
https://github.com/eclipse-vertx/vert.x/blob/master/src/main/java/io/vertx/core/eventbus/impl/CodecManager.java#L99
It is possible, with some limitations and quirks. I would not recommend doing it.
Let's start with the limitations:
It can not be used in clustered mode
You have to declare the codec name every time you send something over the eventbus.
If you create a generic codec that encodes classes with Jackson and every time you send something over the eventbus you make sure to add it using codecName in the deliveryOptions, you can register it only once and use it for all of your classes.
Full example:
fun main() {
val vertx = Vertx.vertx()
vertx.eventBus().registerCodec(GenericCodec())
vertx.eventBus().consumer<Foo>("test-address") {
println(it.body())
it.reply(Bar(), genericDeliveryOptions)
}
vertx.eventBus().request<String>("test-address", Foo(), genericDeliveryOptions) {
println(it.result().body())
}
vertx.close()
}
data class Foo(
val foo: String = "foo",
)
data class Bar(
val bar: String = "bar",
)
class GenericCodec : MessageCodec<Any, Any> {
companion object {
const val NAME = "generic"
}
private val mapper: ObjectMapper = ObjectMapper()
override fun encodeToWire(buffer: Buffer, s: Any) {
buffer.appendBytes(mapper.writeValueAsBytes(s))
}
override fun decodeFromWire(pos: Int, buffer: Buffer): Any {
throw RuntimeException("should never get here, unless using clustered mode")
}
override fun transform(s: Any): Any {
return s
}
override fun name(): String {
return NAME
}
override fun systemCodecID(): Byte {
return -1
}
}
val genericDeliveryOptions = deliveryOptionsOf(codecName = GenericCodec.NAME)

how to resolve - Parameter specified as non-null is null

I have unit test in which I am trying to check is a use case is called with the right parameters but I get an error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method com.xx.xxx.clean.orderview.domain.OnStandUseCaseCoroutine$Params.<init>, parameter serviceType
#Test
fun `when notifyOnStand is called then we create a TimestampedAction with the correct user id, vehicle, timestamp and pass that to the usecase`() {
val actionCaptor = argumentCaptor<TimestampedAction>()
val timestamp = DateTime.now()
every(noServiceRequiredBus.get()).thenReturn(Observable.just(REQUESTED))
every(timingsUpdater.timestampCalculator(any(), any())).thenReturn(timestamp)
baseOrderViewPresenter.setView(view)
baseOrderViewPresenter.notifyOnStand()
runBlocking {
verify(onStandUseCaseCoroutine).run(OnStandUseCaseCoroutine.Params(any(), any(), capture(actionCaptor)))
}
}
Use case which will get called when when called baseOrderViewPresenter.notifyOnStand() from tets case
class OnStandUseCaseCoroutine #Inject constructor(
private val orderRepository: OrderRepository,
private val serviceOrderTypeProvider: ServiceOrderTypeProvider
) : UseCaseCoroutine<GenericResponse, OnStandUseCaseCoroutine.Params> (){
override suspend fun run(params: Params) =
orderRepository.notifyOnStandSuspend(serviceOrderTypeProvider.apiPathFor(params.serviceType), params.id, params.action)
data class Params(val serviceType: String, val id: String, val action: TimestampedAction)
}
Presenter layer which has the call to use case
private fun onstandUseCaseCoroutines(serviceType: String, id: String, action: TimestampedAction, callback: (GenericResponse?) -> Unit) {
try {
onStandUseCaseCoroutine(OnStandUseCaseCoroutine.Params(serviceType, id, action)) {
callback.invoke(it)
}
} catch (exception: Exception) {
onStandResponseErrors()
}
}
how can I fix this please
I tried changing to bellow code but that did not fix it, I am not sure what to do the capture(actionCaptor) bit if that is the issue
runBlocking {
verify(onStandUseCaseCoroutine).run(OnStandUseCaseCoroutine.Params(anyString(), anyString(), capture(actionCaptor)))
}
Any suggestions please
Thanks
R

Continuation<? super String> cannot be applied to () [duplicate]

Assume we have the following suspend function:
suspend fun doSomething(): List<MyClass> { ... }
If I want to call this function in one of my existing Java classes (which I'm not able to convert to Kotlin for now) and get its return value I have to provide a Continuation<? super List<MyClass>> as its parameter (Obviously).
My question is, How can I implement one. Specially its getContext getter.
First, add org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 module to your dependencies. In your Kotlin file define the following async function that corresponds to Java style of writing async APIs:
fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
GlobalScope.future { doSomething() }
Now use doSomethingAsync from Java in the same way as you are using other asynchronous APIs in the Java world.
If you dont want to use org.jetbrains.kotlinx:kotlinx-coroutines-jdk8, I have a new idea.
Write below code in your kotlin project.
#JvmOverloads
fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
}
I write it in my Coroutines class
Then you can call your suspend function like:
Coroutines coroutines = new Coroutines();
UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
(tokenResult, throwable) -> {
System.out.println("Coroutines finished");
System.out.println("Result: " + tokenResult);
System.out.println("Exception: " + throwable);
}
));
login() function is a suspend function.
suspend fun login(username: String, password: String): TokenResult
For your code, you can:
doSomething(getContinuation((result, throwable) -> {
//TODO
}));
Besides, you may want to run your callback code in different thread (e.g. Main thread), just use launch(Dispathers.Main) to wrap resumeWith()
Update: My friend has developed a plugin kotlin-jvm-blocking-bridge that can automatically generate blocking bridges for calling suspend functions from Java with minimal effort, also give it a try.
For coroutines 1.3.0 use this:
BuildersKt.launch(GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
(coroutineScope, continuation) -> suspendFunction(arguments)
);
For java < 8:
BuildersKt.launch(
GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
new Function2<CoroutineScope, Continuation<? super Unit>, Unit/*or your return type here*/>() {
#Override
public Unit/*or your return type here*/ invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
//do what you want
return Unit.INSTANCE; //or something with the defined type
}
}
);
My gradle file:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
Kotlin uses static classes for extension functions, launch is an extension function, so it is defined in BuildersKt. The first parameter is the target of the extension function, the rest are the parameters from the extension functions.
I created interface class based on #Kenvix answer to make it compatible with old Android SDK (lower than API 24)
interface CoroutineCallback<RESULT> {
companion object {
#JvmOverloads
fun <R> call(
callback: CoroutineCallback<R>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
callback.onComplete(result.getOrNull(), result.exceptionOrNull())
}
}
}
}
fun onComplete(result: RESULT?, error: Throwable?)
}
usage
class kotlinClass {
suspend doSomething(foo, bar) : FooBar {}
}
class javaClass {
void doSomething(){
kotlinClassObject.doSomething("foo", "bar", CoroutineCallback.Companion.call((fooBar, error) -> {
//do something with result or error
}));
}
}
now call suspend function from any java class by passing CoroutineCallback

Scala wrapper API for java API

I am new to Scala and I need to have a scala wrapper for my Java API
I have three Java Interfaces
public interface Client<T> {
<T> Future<T> execute(App<T> app);
}
public interface App<T> extends Serializable{
T process(AppContext context) throws Exception;
}
public interface AppContext {
File getDirectory();
void deleteDirectory();
File createDirectory(String path);
}
Following is the Java code to create an App
public class RandomApp extends App<String> {
#Override
public String process(AppContext context) {
// Inorder to access the methods in AppContext I need to access
// it by the following way
appContext.createDirectory("some path");
return "random";
}
}
I want to have a Scala Wrapper for the Client Interface which in turn call the Java API. But I have some modifications for the new Scala API
object ScalaConverter {
implicit class ScalaWrapper(client: Client) {
def scalaClient = new ScalaClient(client)
}
}
class ScalaClient(client: Client) {
def execute[T](appContext: AppContext => T): Future[T] = {
// I am passing appContext as closure instead of passing in
// App because I want to take the advantage of Closures in Scala
// I basically want to create an App with this appContext and
// pass it to the execute method
// For example - but this is not working
var app = // Need to create this app with appContext
Future {
client.execute(app)
}
}
}
If I'm not mistaken, you just want to be able to create App objects from a function that takes a AppContext as parameter and returns a any object (let's say T).
As it's not really interesting to try to mirror the whole java API, just use it as it is, but add some extensions. And to do this you should use implicits.
To do this, I see two possibilities: either add an implicit class on the Client interface to add some functions to it, or add an implicit conversion from (AppContext => T) to App objects.
Let's got with the first solution, you have to embed the implicit class in an object (this can be a package object if you need automatic imports).
object ScalaConverter {
class ScalaApp[T](val block: AppContext => T) extends App[T] {
def process(context: AppContext): T = block(context)
}
implicit class ScalaClient(client: Client) extends AnyVal{
def execute[T](block: AppContext => T): Future[T] = {
client.execute(new ScalaApp(block))
}
}
}
Then, you just have to use your existing java Client object:
import ScalaConverter._
myJavaClient.execute { context =>
???
}
You should get the principle, I maybe made a mistake (did not tried to compile this)

Categories