Is doOnCompleted a valid way to handle token authentication? - android

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*/
}

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.

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.

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).

Related

Rxjava and work manager chained asychronous calls

Update: This was how my old insertIntoDb method looked like which didn't work :
private Completable insertIntoDb(List<ArticleEntity> articleItemEntities) {
return database.articleDao().insertArticles(articleItemEntities)
.observeOn(Schedulers.io());
}
I changed it to the following and now it works :
private void insertIntoDbNew(List<ArticleEntity> articleItemEntities) {
mCompositeDisposable.add(
database.articleDao().insertArticles(articleItemEntities)
.subscribeOn(Schedulers.io())
.subscribe());
}
I don't know why but now it works. Sure the worker completes before the database insert completes but that doesn't seem to be a problem which I believed before.
End of update.
I'm new to reactive programming. My goal is to schedule a work manager to do 4 Actions then return a result using RxJava2. Here are the tasks I want to perform
Do a web api call.
Structure the data we get from the API call.
Insert it into our local room database.
When everything is complete signal Result.success() back to the Job so it knows that everything went ok and can terminate.
So my preferred method would look something like this.
public Result doWork(){
return api.get("URL") responseData -> structureData(responseData) structuredData -> insertIntoDB(structuredData) -> Result.success()
}
I'm using RxJava2 and the RxWorker class.
Below is my current solution. Is this correct or am I doing something wrong?
public class DownloadWorker extends RxWorker {
#Override
public Single<Result> createWork() {
return apiService.download("URL")
.map(response -> processResponse(response))
.doOnSuccess(data -> insertIntoDb(data))
.flatMap(response ->
allComplete()
)
.observeOn(Schedulers.io());
}
Single<Result> allComplete() {
return Single.just(Result.success());
}
}
It behaves like I want it to. It downloads the data, structures it, then inserts it into the DB then returns Result.success(). But I have no idea what I am doing. Am I using RxJava as it was intended?
Also this part bothers me :
.flatMap(response -> allComplete())
the response part is superfluous can I remove it somehow?
I did some improvements to your code:
public class DownloadWorker extends RxWorker {
#Override
public Single<Result> createWork() {
return apiService.download("URL")
.map(response -> processResponse(response))
.flatMapCompletable(articleItemEntities -> database.articleDao().insertArticles(articleItemEntities))
.toSingleDefault(Result.success())
.onErrorReturnItem(Result.failure())
.observeOn(Schedulers.io());
}
}
In original code, you save data using doOnSuccess method which is a side effect. As you mentioned in your comment, insertIntoDb() method returns Completable. Therefore, I changed doOnSuccess(data -> insertIntoDb(data)) to flatMapCompletable(data -> insertIntoDb(data)) which will allow you make sure storing data succeeded and wait until it finishes. As insertIntoDb() method returns Completable and createWork() method has to return Result, we have to now change type from Completable to Single<Result>. Therefore, I used toSingleDefault which returns Result.success() by default. Also, I added onErrorReturnItem(Result.failure()) which will allow RxWorker to track errors. I hope my answer helps.

Publish to specific Observer using PublishSubject

I have a ViewPager with two pages namely Popular and All. What I'm trying to achieve is only push items that have popular tag true to Popular whereas push all items to All.
Currently I have a single class which is used in the PagerAdapter and passing in the page type. How do I filter out PublishSubject so that each page only displays necessary items accordingly.
Both my Observer are subscribed to a single PublishSubject, but I
want to filter when emitting.
Please comment if the question is unclear. I'll try my best to relay this problem. Also sorry if it has already been answered since I couldn't find anything relevant.
The code I'm using is this based on this architecture in which I have a Firebase data store FirebaseSubscriptionDataStore which provides the PublishSubject. This is later subscribed to by SubscribeToSubscriptionUpdates in SubscriptionListPresenterImpl
Thanks in advance.
You can basically define two different methods to get Observable (or Flowable) from PublishSubject. First observable will emit all of the items and second one only popular ones:
public class DataStore {
private PublishSubject<DataItem> dataItemPublishSubject = PublishSubject.create();
public Flowable<DataItem> getAllObservable() {
return dataItemPublishSubject.toFlowable(BackpressureStrategy.BUFFER);
}
public Flowable<DataItem> getPopularObservable() {
return dataItemPublishSubject.toFlowable(BackpressureStrategy.BUFFER)
.filter(new Predicate<DataItem>() {
#Override
public boolean test(DataItem dataItem) throws Exception {
return dataItem.popular;
}
});
}
public static class DataItem {
public final boolean popular;
public DataItem(boolean popular) {
this.popular = popular;
}
}
}
In case you don't want to two methods, you can move .filter() operator everywhere within you Rx chain and you might end up with something like this:
dataStore.getAllObservable()
.doOnNext(new Consumer<DataStore.DataItem>() {
#Override
public void accept(DataStore.DataItem dataItem) throws Exception {
pagerAdapter.addDataAll(dataItem);
}
})
.filter(new Predicate<DataStore.DataItem>() {
#Override
public boolean test(DataStore.DataItem dataItem) throws Exception {
return dataItem.popular;
}
})
.doOnNext(new Consumer<DataStore.DataItem>() {
#Override
public void accept(DataStore.DataItem dataItem) throws Exception {
pagerAdapter.addDataPopular(dataItem);
}
})
.subscribe();

Passing parameter to Observable.create

I am using RXJava on Android for asynchronously access the database.
I want to save an object in my database.
In this way, I created a method which take a final parameter (the object I want to save) and returns an Observable.
At this point I don't care to emit anything so I will call subscriber.onComplete() at the end.
Here is my code:
public Observable saveEventLog(#NonNull final EventLog eventLog) {
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(Subscriber<? super Object> subscriber) {
DBEventLog log = new DBEventLog(eventLog);
log.save();
subscriber.onCompleted();
}
});
}
The thing is, I saw many answer using the final keyword for the parameter, but I would like to do this without it.
The reason is I don't really like the approach of declare a final variable in order to use it in another thread.
Is there any alternative? Thanks.
We usually suggest avoiding the use of create because it may seem simple to use it but they usually violate the advanced requirements of RxJava. Instead, you should use one of the factory methods of Observable. In your case, the just factory method will get what you wanted: no final parameter:
public Observable<?> saveEventLog(#NonNull EventLog eventLog) {
return Observable
.just(eventLog)
.doOnNext(e -> {
DBEventLog log = new DBEventLog(e);
log.save();
})
.ignoreElements();
}

How to get two objects - raw Response object and POJO - all at once with Retrofit?

I'm using Retrofit/OkHttp in a project and recently I've discovered RxJava. Combining it with Retrofit seems easy and straightforward but with regular async callbacks in Retrofit in success(...) we are receiving the parsed POJO and the Response. This is quite useful and in some of my callbacks I'm using both of these objects. I can't seem to find a way to do this with RxJava.
Is it possible to obtain the parsed POJO and the Response object at the same time?
The way RxJava works is that the onNext method always emits exactly one value, so you won't be able to get something like (as it would break the contract):
onNext(T value, Response respone);
The closest could be an Observable<ResponseAndPojo<T>> where ResponseAndPojo is as follows:
public class ResponseAndPojo<T> {
private final T value;
private final Response response;
public ResponseAndPojo(T value, Response response) {
this.value = value;
this.response = response;
}
// add getters here
}
Such an Observable would then emit items with:
onNext(ResponseAndPojo<T> responseAndPojo)
and you would have access to both the Response and the POJO.
Now, how to construct such an Observable:
One way would be to create some kind of Subject (maybe a BehaviorSubject, but for single requests it does not really matter) and then in the Retrofit success method put the return values into the Subject.
So, in some kind of RetrofitWrapper class of your own you would have
public Observable<ResponseAndPojo<YourPojoClass>> getResponseAndPojoObservable() {
final BehaviorSubject<ResponseAndPojo<YourPojoClass>> retrofitSubject = BehaviorSubject.<ResponesAndPojo<YourPojoClass>>create();
yourRetrofitService.getSomething(new Callback<YourPojoClass>() {
#Override
public void success(YourPojoClass pojo, Response response) {
retrofitSubject.onNext(new ResponseAndPojo(pojo, response);
retrofitSubject.onCompleted();
}
});
return retrofitSubject;
}
As you can see, from the outside the Subject looks like an Observable<ResponseAndPojo<YourPojoClass>>, which is exactly what we wanted.

Custom error handling with Retrofit

The REST Api I'm working with has custom codes and messages which are sent from server depending on the state, I would like to implement a custom Callback<T> that calls the success method only if the status code was 0.
Example SUCCESS Response received from server:
{
"code":"0",
"message":"success",
"data": {
"actual_data":"goes_here",
"need_to_construct_objects","from_data"
}
}
Example of FAILURE Response:
{
"code":"301",
"message":"wrong_password",
"data": {
"actual_data":"will_be_null",
"no_need_to_construct_objects","from_data"
}
}
code and message are returned by all requests, the data contains the actual response values, so I would like to do the following:
Check the code and message and only call success() if code is 0.
Call failure() if request failed or code != 0
Construct custom objects based on the data response and pass them via success()
What is the best way to do this? I searched everywhere and could not find a good solution. The only one I got was to let all custom objects have the code and message fields too and check their values inside success(), but this could cause problems in future in case someone forgets to check the code before proceeding.
You can do that quickly by just making an abstract class that implements Callback, and declare your own abstract success and failure methods. The abstract class will handle Retrofit's standard callback methods, interpret the response and call the abstract methods accordingly.
I think another possible approach to this is to override Retrofit's Client interface to build your own Response object.
If you extend OkClient, it can go like this:
public class CustomClient extends OkClient {
#Override public Response execute(Request request) throws IOException {
Response originalRespone = super.execute(request);
int statusCode = 0;
//TODO: read JSON response here (using GSON or similar, and extract status code and message... etc.)
//Convert the status code to HTTP standard status codes, according to the documentation you have.
if(statusCode == 0) statusCode = 200;
//Reconstruct a Response object
return new Response(originalResponse.getUrl(), statusCode, originalResponse.getReason() /*should probably replace with parsed message*/, originalResponse.getHeaders(), originalResponse.getBody());
}
This may be more work than handling your case in Callback, but I think it can help if at some point the API transitions to RESTful API conventions.
This solution comes with its own problem though, because that means the JSON conversion will run twice. One in your client, and another one by Retrofit. Not sure the correct way to do that at the moment. Probably something around TypedInput and a dummy Converter that passes already converted objects.
Create a custom ResponseBodyConverter like this:
public class CustomResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final TypeAdapter<T> adapter;
CustomResponseBodyConverter(TypeAdapter<T> adapter) {
this.adapter = adapter;
}
#Override
public T convert(ResponseBody value) throws IOException,CustomException {
String json = "";
try {
String body = value.string();
json = new JSONObject(body).getJSONObject("data").toString();
int code = new JSONObject(body).getInt("code");
String message = new JSONObject(body).getString("message");
if(code != 0){
throw new CustomException(message);
}
} catch (JSONException e) {
e.printStackTrace();
}
return adapter.fromJson(json);
}
}
It's a better idea to implement a custom callback. You can an example about it below.
public abstract class DefaultRequestCallback<T> implements Callback<T> {
public abstract void failure(Meta meta);
public abstract void success(T responseBean);
#Override
public void success(T baseResponseBean, Response response) {
// You can check your responsebean's error code and
// convert it to a default error
BaseResponseBean bean = (BaseResponseBean) baseResponseBean;
if (bean == null) {
failure(new Meta(ApplicationConstants.ERROR_RETROFIT, "Unknown Error!"));
} else if (bean.getMeta() != null && bean.getMeta().getCode() != ApplicationConstants.RESULT_SUCCESS) {
failure(bean.getMeta());
} else {
success(baseResponseBean);
}
}
#Override
public void failure(RetrofitError error) {
// Convert default error to your custom error.
Meta meta = new Meta(ApplicationConstants.ERROR_RETROFIT, "Error Unknwon");
failure(meta);
}
}
Give your custom callback to your retrofit service interface method.
void yourMethod(DefaultRequestCallback<YourResponseBean> callback);
Good Luck.
This will at least get you started. You can basically create your own custom callback and then handle the success. Look at what was sent and do what you need to.
public class CustomCallback implements Callback {
#Override
public void success(Object o, Response response) {
//Check for success
//if( Success )
//callback.success(o, response);
//else
//Check for error
//callback.failure(error);
}
}
In your case, you can have a class that maps your json response:
class CustomResponse {
String code;
String message;
Data data;
static class Data {
String actualData;
String needToContructObjects;
String noNeedToContructObjects;
}
}
Then, since you're back to the java objects world, you can have a factory-like object inside your success method callback that creates the desired object based on the returned custom response. If you want to get this response in the failure callback, I'd reconsider using Retrofit, since your API is not following a good Rest design.
Although this is plenty possible, and understanding you might not be involved on the API development, be aware this is not a good API design approach. If you are POSTing a login request to the server, you can understand this request as a request to create a resource (an authenticated user session, for instance). If you don't send the correct parameters (the correct username and password in this specific case), the server should reject the resource creation request and send back a 4-hundred-something (4xx) http status code indicating your request was not correct somehow. Retrofit would understand this 4xx status code and call your failure callback, where you could handle the response appropriately.

Categories

Resources