I'm trying to convert a callback hell to RX but I'm stuck with getting the proper order, below is my functionality I want to achieve
a) User Login-> get the Auth Cookies, if login credentials invalid show error message
b) use the Auth Cookies to get Customer Type,
c) if the Customer Type is zero/ show profile Restricted Error Message and log out the user
d) if the customerType, not zero proceed to get the other customer Details
e) if any of the customer APIs returns an error response, log out the user and show login failure message
f) if all customer API success show the home screen
API
Login
@FormUrlEncoded
@POST("distauth/UI/Login")
Single<Response<Void>> doLogin1(@Field("username") String username, @Field("password") String password,
@Field("rememberme") String rememberMe, @Field("answer") String answer,
@QueryMap Map<String, String> options);
public Single<Boolean> doLogin(@NonNull String username, @Nullable String password) {
return authapi.doLogin1(username, password, "y", "", logiOptions)
.flatMap(new Function<Response<Void>, SingleSource<Boolean>>() {
@Override
public SingleSource<Boolean> apply(Response<Void> response) throws Exception {
if (response.code() == HttpStatus.MOVED_TEMPORARILY.value()
&& !StringUtils.isEmpty(Session.getCookie())
) {
return Single.just(true);
}
throw new Exception("Invalid Login Details");
}
});
}
//==========
Logout
@FormUrlEncoded
@POST("distauth/UI/Logout")
@Headers("Cache-Control: no-cache")
Completable doLogout(@Field("logout") boolean logout); //return 302 HTTP Status code with empty iPlanetCookie
//==========
NOTE: Loing/logout is not a REST API, this legacy app implement as Form Post ;) so when the success of login return 302 with cookies, and log out also return 302 as status code
Get Customer Details
Single<CustomerAccountVO> getCustomerAccountDetails(boolean forceRefresh);
//==========
Single<CustomerType> getCustomerUserProfile(boolean forceRefresh);
@Override
public Single<CustomerType> getCustomerUserProfile(boolean applyResponseCache) {
return this.mCustomerRemoteDataStore.getCustomerUserProfile(applyResponseCache)
.doOnSuccess(new Consumer<CustomerType>() {
@Override
public void accept(CustomerType customerType) throws Exception {
if (customerType != null && customerType.getBody() != null &&
!StringUtils.isEmpty(customerType.getBody())) {
if (customerType.getBody().equalsIgnoreCase(AppConfig.ERROR)) {
throw new CustomerProfileNotFound(500, "user account restrictions");
} else {
mCustomerLocalRepository.saveCustomerType(customerType);
}
}
}
}).doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG, "error occurred while getting customer user profile", throwable);
}
});
}
//==========
Single<CustomerAccountId> getAccountId(boolean forceRefresh);
//==========
Single<Customer> getCustomer(boolean forceRefresh);
//==========
Get Customer Full Details
Single<CustomerDetails> getCustomerFullDetails(boolean applyResponseCache);
Implementation:
@Override
public Single<CustomerDetails> getCustomerFullDetails(boolean forceRefresh) {
Single<CustomerDetails> customerDetails = Single.zip(
getCustomerUserProfile(forceRefresh).subscribeOn(Schedulers.io()),
getAccountId(forceRefresh).subscribeOn(Schedulers.io()),
getCustomerAccountDetails(false).subscribeOn(Schedulers.io()),
getCustomer(forceRefresh).subscribeOn(Schedulers.io()), new Function4<CustomerType, CustomerAccountId,
CustomerAccountVO, Customer, CustomerDetails>() {
@Override
public CustomerDetails apply(@NonNull CustomerType customerType,
@NonNull CustomerAccountId customerAccountId,
@NonNull CustomerAccountVO customerAccountVO,
@NonNull Customer customer) throws Exception {
return CustomerDetails.builder().customerType(customerType).customerAccountVO
(customerAccountVO).customer(customer).customerAccountId(customerAccountId).
build();
}
});
return customerDetails;
}
//==========
Each customer request is independent so I thought to execute as sperate thread and zip the final result/
Single<BaseServerResponse> updateCustomerDetails(@Nonnull boolean secure, int secureRequestCode, @Nonnull JSONObject customerContact);
//Presenter Implementation: this implementation not working as i expect above, can some one help me to get this correct,
public void doLoginHandler(@NonNull String username, @NonNull String password) {
checkViewAttached();
getMvpView().showLoadingIndicator();
addSubscription(
apiService.doLogin2(username, password)
.subscribeOn(Schedulers.io())
.flatMap(new Function<Boolean, SingleSource<CustomerDetails>>() {
@Override
public SingleSource<CustomerDetails> apply(Boolean aBoolean) throws Exception {
if (aBoolean) {
//get customr Full Details
Log.d(TAG, "apply: "+aBoolean);
return customerRepository.getCustomerFullDetails(true);
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(new Function<Throwable, SingleSource<? extends CustomerDetails>>() {
@Override
public SingleSource<? extends CustomerDetails> apply(Throwable throwable) throws Exception {
if (throwable instanceof CustomerProfileNotFound) {
getMvpView().showUserProfileAccessRestrictMessage();
} else {
getMvpView().onLoginAuthFailure();
}
return Single.just(CustomerDetails.builder().errorOccurred(true).build());
}
})
.flatMapCompletable(new Function<CustomerDetails, CompletableSource>() {
@Override
public CompletableSource apply(CustomerDetails customerDetails) throws Exception {
if(customerDetails.isErrorOccurred()){
return apiService.doLogout();
}
return Completable.complete();
}
})
.subscribe(new Action() {
@Override
public void run() throws Exception {
getMvpView().onLoginAuthSuccess();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
if (throwable instanceof CustomerProfileNotFound) {
getMvpView().showUserProfileAccessRestrictMessage();
} else {
getMvpView().onLoginAuthFailure();
}
}
}));
}