confusion with onExceptionResumeNext passing a observable as a lambda expression - kotlin

io.reactivex.rxjava2:rxjava:2.1.13
kotlin_version = '1.2.30'
I have the following Observable and I am trying to throw an exception to test the capture of the exception in OnError. However, when I pass in the following to the onExceptionResumeNext(Observable.just(10)) I get the following output:
1
2
10
onComplete
fun main(args: Array<String>) {
Observable.fromArray(1, 2, 0, 4, 5, 6)
.doOnNext {
if (it == 0) {
throw RuntimeException("Exception on 0")
}
}
.onExceptionResumeNext(Observable.just(10))
.subscribe(
{
println(it)
},
{
println("onError ${it.message}")
},
{
println("onComplete")
} )
}
However, If I pass a lambda expression to that method I get the following output:
1
2
Observable.fromArray(1, 2, 0, 4, 5, 6)
.doOnNext {
if (it == 0) {
throw RuntimeException("Exception on 0")
}
}
.onExceptionResumeNext { Observable.just(10) }
.subscribe(
{
println(it)
},
{
println("onError ${it.message}")
},
{
println("onComplete")
} )
}
Just wondering what is the difference between doing this onExceptionResumeNext(Observable.just(10)) and doing this onExceptionResumeNext { Observable.just(10) }
Many thanks in advance

in the first case you passing observable to onExceptionResumeNext
in case of error in original observable, you'll be resubscribed to observable that is passed to onExceptionResumeNext
that's why you're getting "1" and "2" from original source and then "10" and "onComplete" from Observable.just(10)
in second case, you're using lambda syntax, so you're effectively doing this:
.onExceptionResumeNext(object : ObservableSource {
void subscribe(#NonNull Observer<? super T> observer) {
Observable.just(10)
}
})
as you can see, observer that is passed to you is not getting subscribed. Thus, nothing is emmited after "1" and "2" from initial source
what you should be doing is
.onExceptionResumeNext { observer ->
Observable.just(10).subscribe(observer)
}

Related

RxJava how to handle Error without Quit flow

My scenario is like this:
During a flow ,if there is an error happened during an element processing (in this example element is "three"), I still want keep processing the others.
In this example: I want to print "1,2,4,5" however, it just print "1 ,2".
Observable<String> numbers = Observable.just("1", "2", "three", "4", "5");
numbers.map(v->{
return Integer.parseInt(v);
}).onErrorResumeNext(error->{return Observable.just(-1);})
.filter(v-> {
System.out.println("filter value smaller than 0");
return v>0;
})
.subscribe(s -> {
System.out.println(s);
});
}
I checked doc, "onErrorResumeNext" will instead relinquish control to the Observable returned from resumeFunction.
Is there way to print "1,2,4,5"?
The flow stops because there is a crash in map. The only way to avoid the flow being stopped is to not let map crash in your example. Put the parseInt into a try-catch and return -1 from the catch part.
Observable<String> numbers = Observable.just("1", "2", "three", "4", "5");
numbers.map(v -> {
try {
return Integer.parseInt(v);
} catch (NumberFormatException ex) { // <--------------------------------------
return -1;
}
})
.filter(v -> {
System.out.println("filter value smaller than 0");
return v > 0;
})
.subscribe(s -> {
System.out.println(s);
});

Custom directive to check list length for input types

I tried my best to write a custom directive in apollo server express to validate if an input type field of type [Int] does not have more than max length but do not know if its the right way to do. Appreciate if somebody could help me correct any mistakes in the code below.
// schema.js
directive #listLength(max: Int) on INPUT_FIELD_DEFINITION
input FiltersInput {
filters: Filters
}
input Filters {
keys: [Int] #listLength(max: 10000)
}
// Custom directive
const { SchemaDirectiveVisitor } = require('apollo-server-express');
import {
GraphQLList,
GraphQLScalarType,
GraphQLInt,
Kind,
DirectiveLocation,
GraphQLDirective
} from "graphql";
export class ListLengthDirective extends SchemaDirectiveVisitor {
static getDirectiveDeclaration(directiveName) {
return new GraphQLDirective({
name: directiveName,
locations: [DirectiveLocation.INPUT_FIELD_DEFINITION],
args: {
max: { type: GraphQLInt },
}
});
}
// Replace field.type with a custom GraphQLScalarType that enforces the
// length restriction.
wrapType(field) {
const fieldName = field.astNode.name.value;
const { type } = field;
if (field.type instanceof GraphQLList) {
field.type = new LimitedLengthType(fieldName, type, this.args.max);
} else {
throw new Error(`Not a scalar type: ${field.type}`);
}
}
visitInputFieldDefinition(field) {
this.wrapType(field);
}
}
class LimitedLengthType extends GraphQLScalarType {
constructor(name, type, maxLength) {
super({
name,
serialize(value) {
return type.serialize(value);
},
parseValue(value) {
value = type.serialize(value);
return type.parseValue(value);
},
parseLiteral(ast) {
switch (ast.kind) {
case Kind.LIST:
if (ast.values.length > maxLength) {
throw {
code: 400,
message: `'${name}' parameter cannot extend ${maxLength} values`,
};
}
const arrayOfInts = ast.values.map(valueObj => parseInt(valueObj['value']));
return arrayOfInts;
}
throw new Error('ast kind should be Int of ListValue')
},
});
}
}
Does this look right?
Thanks

How to use Serde to parse a field that might fail to be deserialized without failing the entire deserialization?

I am deserializing some JSON objects which come in as requests. The input body is nested, but a certain field is sometimes misformatted for a variety of reasons. In that situation I still want the rest of the object. This doesn't all have to be done through serde; but what is happening now, is that if a single subfield is messed up, the whole request is trashed. I want to somehow still deserialize that result and just mark the field as errored out. How can this be done?
E.g. the data schema might look like:
struct BigNested {
a: Vec<A>,
b: B, // definition omitted
}
struct A {
keep_this: Foo,
trouble: SometimesBad,
}
trouble is the field that's frequently coming in messed up. I would be happy to (e.g.) turn trouble into a Result<SometimesBad, Whatever> and process it from there, but I don't know how to get serde to let me do that.
certain field is sometimes misformatted
You didn't say how malformed the incoming JSON was. Assuming it's still valid JSON, you can pull this off with Serde's struct flatten and customized deserialization:
The customized deserialization is done in a way that never fails for valid JSON input, although it may not return value of expected type if the input has unexpected format.
But these unexpected fields still need to go somewhere. Serde's struct flatten comes in handy here to catch them since any JSON snippet can be deserialized to a HashMap<String, Value>.
//# serde = { version = "1.0.103", features = ["derive"] }
//# serde_json = "1.0.44"
use serde::{Deserialize, Deserializer, de::DeserializeOwned};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Deserialize, Debug)]
struct A {
keep_this: Foo,
trouble: SometimesBad,
}
#[derive(Deserialize, Debug)]
struct Foo {
foo: i32,
}
#[derive(Deserialize, Debug)]
struct SometimesBad {
inner: TryParse<Bar>,
#[serde(flatten)]
blackhole: HashMap<String, Value>,
}
#[derive(Deserialize, Debug)]
struct Bar {
bar: String,
}
#[derive(Debug)]
enum TryParse<T> {
Parsed(T),
Unparsed(Value),
NotPresent
}
impl<'de, T: DeserializeOwned> Deserialize<'de> for TryParse<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
match Option::<Value>::deserialize(deserializer)? {
None => Ok(TryParse::NotPresent),
Some(value) => match T::deserialize(&value) {
Ok(t) => Ok(TryParse::Parsed(t)),
Err(_) => Ok(TryParse::Unparsed(value)),
},
}
}
}
fn main() {
let valid = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": "one"}}}"#;
println!("{:#?}", serde_json::from_str::<A>(valid));
let extra_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": "one"}, "extra": 2019}}"#;
println!("{:#?}", serde_json::from_str::<A>(extra_field));
let wrong_type = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "bar": 1}}}"#;
println!("{:#?}", serde_json::from_str::<A>(wrong_type));
let missing_field = r#"{ "keep_this": { "foo": 1 }, "trouble": { "inner": { "baz": "one"}}}"#;
println!("{:#?}", serde_json::from_str::<A>(missing_field));
let missing_inner = r#"{ "keep_this": { "foo": 1 }, "trouble": { "whatever": { "bar": "one"}}}"#;
println!("{:#?}", serde_json::from_str::<A>(missing_inner));
}
(The credit isn't all mine. Serde's issue 1583 basically has everything.)

RxJava Maybe: Any neat way to handle Empty case?

I am stuck at this problem, which should be fairly simple. I need subscriber to execute a code block when the Maybe has completed as an Empty Maybe. I found that
we can pass default Maybe value or use switchIfEmpty but I feel both are hacky.
Also there is a subscribe function which takes onComplete function (along with handlers for other two events), but onComplete does not take any argument which can be used to find if maybe was completed empty.
Another way could be Maybe.isEmpty.blockingGet(), but it is dirty too.
I have tried following (Kotlin Syntax):-
fun <T> Maybe<T>.subscribeWithEmptyHandler(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit, onEmpty: () -> Unit) {
this.isEmpty.subscribe({ if (it) onEmpty() }, { onError(it) })
this.subscribe({ onSuccess(it) }, { onError(it) })
}
But as expected it is running subscription twice, tested here:-
Maybe.create<Int> {
println("subscribing")
//Remove line below to create Empty Maybe
it.onSuccess(5)
it.onComplete()
}
.subscribeWithEmptyHandler({println("success")},{println("error")},{println("empty")})
Could somebody please suggest neater way to solve this?
Use Maybe.doOnEvent (java example):
Maybe
.empty()
.doOnEvent((value, error)-> {
if (value==null && error == null) {
System.out.println("empty!");
}})
.subscribe();
There is a solution using flatMap
return Maybe.just<String>(smth)
.flatMap(
Function {
Maybe.just(it) // onSuccess
},
Function {
Maybe.error(it) // onError
},
Callable { // onComplete
Maybe.just("Empty")
}
)
Or
return Maybe.just<String>(smth)
.flatMap(
{
Maybe.just<String>(it) // onSuccess
},
{
Maybe.error(it) // onError
},
{
Maybe.just("Empty") // onComplete
}
)
I did the following which is neater than any I wrote in the question:-
fun <T> Maybe<T>.subscribeWithEmptyHandler(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit, onEmpty: () -> Unit) {
this.toSingle()
.subscribe(
{ onSuccess(it) },
{ if (it is NoSuchElementException) onEmpty() else onError(it) }
)
}
Here it subscribes only once and doesn't involve creating new default values. Still not sure if this is the best way.

RxJava Flowable.Interval backpressure when flatmap with single

I'm having a scenario where I need to periodically call an API to check for a result. I'm using Flowable.interval to create an interval function which calls the API.
However, I'm having trouble with backpressure. In my example below, a new single is created on each tick in the interval. The desired effect is to only call the API if a call is not already in progress
Flowable.interval(1, 1, TimeUnit.SECONDS).flatMap {
System.out.println("Delay $it")
//simulates API call
Single.just(1L).doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE SUCCESS!!!")
}.toFlowable()
}.subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()).blockingFirst()
I can solve this using a filter variable like so:
var filter = true
Flowable.interval(1, 1, TimeUnit.SECONDS).filter {
filter
}.flatMap {
System.out.println("Delay $it")
Single.just(1L).doOnSubscribe {
filter = true
}.doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE!!!")
filter = true
}.toFlowable()
}.subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()).blockingFirst()
But it seems like a hacky solution. I've tired applying onBackPressureDrop after the interval function, but it has no effect.
Any suggestions?
You have to constrain flatMap as well:
Flowable.interval(1, 1, TimeUnit.SECONDS)
.onBackpressureDrop()
.flatMapSingle({
System.out.println("Delay $it")
//simulates API call
Single.just(1L).doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE SUCCESS!!!")
}
}, false, 1) // <----------------------------------------------------------
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.subscribe()