Managing Auth Token using RxJava - android

I've been wondering what's the best way to tackle the issue of token refresh.
I'm connecting to an API which supplies me with a auth-token, if sometime time during the calls i get a INVALID_AUTH i need to re-authenticate.
So for the naive implementation i did this
#SuppressWarnings("unchecked")
#Override
public Observable<User> getUsers() {
return runCommandAndrefreshAuthIfNecessary(new RequestCommand() {
#Override
public Observable create() {
return createService(UsersApi.class).getUsers();
}
});
}
private Observable runCommandAndrefreshAuthIfNecessary(final RequestCommand command) {
return command.create()
.onErrorResumeNext(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
return handleRefreshToken(command);
}
});
}
private Observable<?> handleRefreshToken(final RequestCommand command) {
return refreshToken().flatMap(new Func1<Boolean, Observable<?>>() {
#Override
public Observable<?> call(Boolean aBoolean) {
return command.create();
}
});
}
As you can see i'm just wrapping the retrofit command, if i get an error i run refreshToken(), the token refreshes and i run the retrofit command again, so finally the Observable is passed back to the subscriber. Works as expected.
The thing i'm struggling with, is what happens i a multiple calls are made, for example, i'm calling getUsers and getFlags one after another. both of them get the INVALID_AUTH, currently both of the fire refreshToken(), which is bad.
i'm looking for a rx-java way to manage the calls, meaning after the first call of getUsers fires refreshToken, any call after that needs to wait for the refreshToken to end, only then fire the retrofit command.
Any suggestion will be appreciated.

You can use .cache() on the Observable for the token refreshing:
http://reactivex.io/documentation/operators/replay.html

Related

how to fetch observables in parallel, wherein only one api call has a retry logic

I want to implement a logic using RxJava in my android application, which requires three parallel api calls. Only the third api call has a retry logic. If, after having three attempts, the success is achieved then a subsequent call will be made for the fourth api, else only the result of first and second api calls will be passed on to the subscriber.
I tried to achieve this using Zip operator but then got stuck with retry logic for third api call.
Observable<String> observable1 = Observable.just("A","B");
Observable<Integer> observable2 = Observable.just(1,2);
Observable<Boolean> observable3 = Observable.just(Boolean.TRUE, Boolean.FALSE);
Observable.zip(observable1, observable2, observable3, new Function3() {
#Override
public Object apply(String s, Integer integer, Boolean aBoolean) throws Exception {
if (aBoolean==null){
alphabets3.retry(3).doOnComplete(new Action() {
#Override
public void run() throws Exception {
// the result will never be used
}
});
}
return s+integer+aBoolean;
}
}).subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
Log.e("onNext-->", o.toString());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
if any Observable failed in the Zip operator, Zip will fail the stream, the only way I know to achieve parallel execution and error handling with Zip, is to add onErrorResumeNext to each Observable, that map the error to a new model to deal with later .. and handling what you want to do in the zip mapping function ... for example
Obsevable.zip(
observable1.onErrorResumeNext{Observable.just(Model(it)},
observable2.onErrorResumeNext{Observable.just(Model(it)},
observable3.retryWhen {t is TimeOutException} //here you can add your retry logic
.onErrorResumeNext(t -> Observable.just(Model(t)),(m1 , m2, m3) -> Result())

RxJava flatmap: how to check which observable emitted an error

I'm using RxJava's flatmap in order to execute multiple calls in sequence where one call relys on the previous call. I also need to know which observable emitted an error in the case that onError is called in order to properly implement my error handling. How do I achieve this?
Here is my code:
mSubscription = RxUtil.callObservable(mDataManager.createAccount(email, password))
.flatMap(new Func1<AuthResult, Observable<Void>>() {
#Override
public Observable<Void> call(AuthResult authResult) {
User user = new User(0, null, null, name, null, username, 0, 0);
return RxUtil.callObservable(mDataManager.createUser(authResult.getUser().getUid(), user));
}
})
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
try {
throw (Exception) throwable;
} catch (FirebaseAuthUserCollisionException ucException) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_exists));
} catch (Exception exception) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_general));
}
}
#Override
public void onNext(Void aVoid) {
getPickUpView().createAccountSuccessful(authResult);
}
});
I was thinking about this the wrong way. Is summary, I thought this was an issue that I needed to address when i didn't. RxJava will emit all errors in the onError method no matter what observable emits the error. Once onError is called the subscription is done, so the flatmap call will never take place.
In summary, all I need to do is handle my errors from both observables I call (the original and the one in the flatmap) in the same onError method.

Getting variables "into" RxJava chain for use later

I am using RxJava on Android to perform a login operation.
I need to pass in a username, password and a boolean flag. The username and password and sent to a server for verification and once a response is returned I need to use the flag to determine what to do next.
Since the login operation is asynchronous, I want to ensure that when the response returns that I will still have access to the username, password and flag that I passed in at the beginning.
Here is the initial way I coded this up that I believe has problems:
public Observable<Result> execute1(final String username, final String password, final boolean shouldSaveUsername) {
return mLoginNetwork
.loginWithCredentials(username, password)
.map(new Func1<Response<Void>, LoginObject>() {
#Override
public LoginObject call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return new LoginObject(username, password, shouldSaveUsername);
}
})
.doOnNext(new Action1<LoginObject>() {
#Override
public void call(LoginObject loginObject) {
if (loginObject.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<Entitlement, Observable<Result>>() {
#Override
public Observable<Result> call(LoginObject loginObject) {
return mNetwork
.fetchSomething();
}
});
}
When I call execute1() it returns an Observable which I cache and then subscribe to. If an Android configuration change occurs I unsubscribe from the Observable but keep it in a cache. Once the configuration change is complete I take the Observable out of the cache and resubscribe to it. When I resubscribe the loginWithCredentials call would need to be made again, but when it returns the username, password and boolean flag would no longer exist and therefore I wouldn't be able to use them in my chain which is a problem.
So, how to solve this issue?
I need a way for the input data to the Observable to become part of the Observable so that when I cache the Observable the input data is also cached.
Here is a proposed solution below:
public Observable<Result> execute2(String username, String password, boolean shouldSaveUsername) {
return Observable
.just(new LoginData(username, password, shouldSaveUsername))
.flatMap(new Func1<LoginData, Observable<LoginData>>() {
#Override
public Observable<?> call(final LoginData loginData) {
return mLoginNetwork
.loginWithCredentials(loginData.getUsername(), loginData.getPassword())
.map(new Func1<Response<Void>, LoginData>() {
#Override
public LoginData call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return loginData;
}
});
}
})
.doOnNext(new Action1<LoginData>() {
#Override
public void call(LoginData loginData) {
if (loginData.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<LoginData, Observable<Result>>() {
#Override
public Observable<Result> call(LoginData loginData) {
return mNetwork
.fetchSomething();
}
});
}
What I'm attempting to do is to make the input data part of the stream right away by using Observable.just() to take the input data and make it into an Observable and then let the rest of the downstream operations receive it as an input. I assume that if I now cache the observable and resubscribe later that the input data is now embedded in my observable and can be accessed in any of the operators later.
Have I solved my problem in my proposed solution in a "normal" RxJava / functional way? Are there better ways to approach this problem?
The username/password/save-flag are passed in to execute1() as parameters, marked as final. Then, in your anonymous nested classes, you make explicit references to those values, "closing over" them. The resulting observable chain has everything bound to it that it needs in order to operate.
Subscribing to the observable again will use the original username/password/save-flag.

Callback Hell: Sequencing RESTful Volley requests? RxAndroid?

I'd like to see an Android java example of how to sequence a chain of async (= nonblocking) RESTful Volley requests.
Is this what RxAndroid is used for?
If so, I'd like to see the example using RxAndroid.
If not, I'd still like to see a good example w/out diving into CALLBACK HELL!
I tried to do so but ended up in CBHell:
Need to send multiple Volley Requests - in a sequence
I want my result from my 1st request to be used in the 2nd request. Then the result from the 2nd request I want used in the 3rd request. Please, how do I chain such Volley requests?
You could use Rx to chain multiple requests by using the flatMap method.
flatMap requires you to return another Observable of the type of your chosing thus allowing you do something async with another type.
All of the examples below are made with the new rx v2. But all methods and mechanics also apply to v1
Example:
final MyVolleyApi api = new MyVolleyApi();
api.getName()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<String, ObservableSource<Integer>>() {
#Override
public ObservableSource<Integer> apply(String name) throws Exception {
return api.getAgeForName(name);
}
})
.flatMap(new Function<Integer, ObservableSource<Date>>() {
#Override
public ObservableSource<Date> apply(Integer age) throws Exception {
return api.getYearOfBirthForAge(age);
}
})
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
// handle the exception that occurred during one of the api calls
}
})
.subscribe(new Consumer<Date>() {
#Override
public void accept(Date date) throws Exception {
// do something with the 3rd argument here
}
});
This is the MyVolleyApi dummy class:
public class MyVolleyApi {
public Observable<String> getName() {
return Observable.just("Rx");
}
public Observable<Integer> getAgeForName(String name) {
return Observable.just(24);
}
public Observable<Date> getYearOfBirthForAge(int age) {
return Observable.just(new Date());
}
}
This could apply to anything, it's not volely specific at all

RxJava re-create observable onError

So I have my form model which holds all data I want to validate and then send to the server. Let's keep it as simple as possible - isFormValid or api request should return Observable.errr(throwable) which should call onError() in the subscriber.
public void submitForm(Form form){
Observable
.just(form)
.flatMap(form->{
if(isFormValid(form))
return Observable.just(form);
else
return Observable.error(someValidationError);
})
.flatMap(form->{
Request req = new Request(form);
try{
return Observable.just(getResponseFrom(req));
}
catch(ApiException e){
return Observable.error(e)
}
}).subscribe(
new Subscriber<ResponseModel>(){
#Override
public void onComplete(){}
#Override
public void onError(Throwable t){}
#Override
public void onNext(ResponseModel model){}
}
);
}
Ok, now let's say user enters invalid data, submitForm() is called and -sure enought- onError is called in subscriber and then onComplete. The user then enters valid data and submitForm() is called again.
Now here's the problem - in the second submitForm() call nothing happens! At least flatMap Func1 and the second flatMap Func2 are not called.
Why? What am I doing wrong ? Is it an architectural flaw?

Categories

Resources