0

I was wondering about token authentication with Retrofit/RxJava. I was refactoring my code to use a DataManager, such that the activity evokes a method in the presenter, the presenter subscribes to the datamanager.getTrips which is then responsible for the call to the api.

I want to make sure that my accesstoken is valid, and if it is not generate it first and then complete the task. Would doOnCompleted be a good way of achieving this or is there a better way?

/*In DataManager, service is the REST Interface*/
public Observable<VtTripResponseModel> getTrips(final String start, final String end, final String time, final String date){
        if(accessToken.isValid()){
            return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
        }
        else{
            generateAccessToken().doOnCompleted(new Action0() {
                @Override
                public void call() {
                    getTrips(start, end, time, date);
                }
            });
        }

/*What do I return here? Since this will not be reached*/
    }
buddhabath
  • 664
  • 1
  • 10
  • 22

3 Answers3

4

To elaborate on @david.mihola 's answear you could do it like this:

Observable.just(accessToken.isValid())
    .flatMap(valid -> {
        if(valid){
            return Observable.just(accessToken);
        } else {
            return generateAccessToken();
        })
    .flatMap(token -> service.getTrip(token.getAccessString(),start,end,time,date,JSON_PARAM)) 

So that the first flatMap generates token if it is not valid and if it is, then simply passes it on(this assumes that generateToken() returns Observable<AccessToken>). Second flatMap is just the thing that you wanted to do.

MatBos
  • 2,380
  • 24
  • 34
2

And to give some context to @MatBos's elaboration on my comment, especially with regard to your question

What do I return here? Since this will not be reached

It felt quite eye-opening for me when I realized that an Observable (or at least a cold-one, like the one we are talking about here, and the one that @MatBos described in his answer) is essentially a description of a future computation.

Returning an Observable just means that you return a blue-print for what should happen if and when someone (i. e. the code that called your getTrips method) actually subscribes to that Observable. Of course, an Observable is also an asynchronous stream of events, but I think that my description here is valid, too.

So, what do you return? A description that says:

If someone subscribes
  1. First check if we have valid access token
  2. a) If we do, just forward the access token for later use
     b) If we don't, generate a new one access token and forward that
  3. Take whatever access token you get - it is now guaranteed to be valid and use to retrieve the trips.
  4. Forward them to the subscriber when they are ready.

And that description is exactly the Observable that @MatBos described.

david.mihola
  • 12,062
  • 8
  • 49
  • 73
1

Thank you for the input, In the meantime I was flying away and found a similar, but formulated in another way post: Retrofit with RxJava which had an answer in it.

My code now looks like:

/*In DataManager*/
 public Observable<VtTripResponseModel> getTrips(String start, String end, String time, String date){
            return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
    }

public Observable<VtResponseModel> getLocationByInput(String input){
        return service.getLocationByInput(accessToken.getAccessString(),input,JSON_PARAM);
    }

/*SF 26201420*/
    public <T> Func1<Throwable,? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) {
        return new Func1<Throwable, Observable<? extends T>>() {
            @Override
            public Observable<? extends T> call(Throwable throwable) {
                // Here check if the error thrown really is a 401
                if (isHttp401(throwable)) {
                    return service.getAccessToken(CREDENTIALS, DEVICE).flatMap(new Func1<AccessToken, Observable<? extends T>>() {
                        @Override
                        public Observable<? extends T> call(AccessToken token) {
                            accessToken = token;
                            return toBeResumed;
                        }
                    });
                }
                // re-throw this error because it's not recoverable from here
                return Observable.error(throwable);
            }
        };
    }

And the method in my presenter now looks like

public void loadRepositories(String search){
    subscription = manager.getLocationByInput(search)
              .onErrorResumeNext(manager.refreshTokenAndRetry(Observable.defer(() -> manager.getLocationByInput(search))))
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeOn(application.defaultSubscribeScheduler())
                    .subscribe(new Subscriber<VtResponseModel>() {... etc}

Now when the first call is made after starting the application, it will generate an accesstoken since I recieve a 401. Then that accesstoken is stored within the manager, and reused until there is a new 401 (after it has expired).

Community
  • 1
  • 1
buddhabath
  • 664
  • 1
  • 10
  • 22