Android RXJava句柄链api调用:显示不同的错误消息并继续流

Android RXJava句柄链api调用:显示不同的错误消息并继续流,android,rx-java,retrofit,rx-java2,rx-android,Android,Rx Java,Retrofit,Rx Java2,Rx Android,我正试图将一个回调地狱转换为RX,但我一直无法获得正确的顺序,下面是我想要实现的功能 a) 用户登录->获取身份验证Cookies,如果登录凭据无效,则显示错误消息 b) 使用Auth Cookie获取客户类型 c) 如果客户类型为零/显示配置文件受限错误消息并注销用户 d) 如果customerType为非零,则继续获取其他客户详细信息 e) 如果任何客户API返回错误响应,请注销该用户并显示登录失败消息 f) 如果所有客户API成功,则显示主屏幕 API Login @For

我正试图将一个回调地狱转换为RX,但我一直无法获得正确的顺序,下面是我想要实现的功能

a) 用户登录->获取身份验证Cookies,如果登录凭据无效,则显示错误消息

b) 使用Auth Cookie获取客户类型

c) 如果客户类型为零/显示配置文件受限错误消息并注销用户

d) 如果customerType为非零,则继续获取其他客户详细信息

e) 如果任何客户API返回错误响应,请注销该用户并显示登录失败消息

f) 如果所有客户API成功,则显示主屏幕

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();
                            }
                        }
                    }));


}
API
登录
@FormUrlEncoded
@POST(“distauth/UI/Login”)
单个doLogin1(@Field(“用户名”)字符串用户名,@Field(“密码”)字符串密码,
@字段(“rememberme”)字符串rememberme,@Field(“answer”)字符串answer,
@查询映射选项);
公共单个doLogin(@NonNull字符串用户名,@null字符串密码){
返回authapi.doLogin1(用户名、密码、“y”、“logiOptions”)
.flatMap(新函数(){
@凌驾
公共单源应用(响应)引发异常{
if(response.code()==HttpStatus.MOVED_temporary.value())
&&!StringUtils.isEmpty(Session.getCookie())
) {
返回单曲。just(true);
}
抛出新异常(“无效登录详细信息”);
}
});
}
//==========
注销
@FormUrlEncoded
@发布(“distauth/UI/Logout”)
@标题(“缓存控制:无缓存”)
可完成的doLogout(@Field(“logout”)布尔logout)//返回302 HTTP状态代码,其中iPlanetCookie为空
//==========
注意:Loing/logout不是REST API,此遗留应用程序实现为表单Post;)所以当登录成功时返回302带有cookie,并且注销时也返回302作为状态码
获取客户详细信息
单一getCustomerAccountDetails(布尔值强制刷新);
//==========
单个getCustomerUserProfile(布尔forceRefresh);
@凌驾
公共单getCustomerUserProfile(布尔applyResponseCache){
返回此.mcCustomerRemoteDataStore.getCustomerUserProfile(applyResponseCache)
.doOnSuccess(新消费者(){
@凌驾
public void accept(CustomerType CustomerType)引发异常{
if(customerType!=null&&customerType.getBody()!=null&&
!StringUtils.isEmpty(customerType.getBody()){
if(customerType.getBody().equalsIgnoreCase(AppConfig.ERROR)){
抛出新CustomerProfileNotFound(500,“用户帐户限制”);
}否则{
mcCustomerLocalRepository.saveCustomerType(customerType);
}
}
}
}).doOnError(新使用者(){
@凌驾
public void accept(Throwable Throwable)引发异常{
Log.e(标签“获取客户用户配置文件时出错”,可丢弃);
}
});
}
//==========
单个getAccountId(布尔forceRefresh);
//==========
单getCustomer(布尔forceRefresh);
//==========
获取客户的全部详细信息
单getCustomerFullDetails(布尔applyResponseCache);
实施:
@凌驾
公共单getCustomerFullDetails(布尔forceRefresh){
单个customerDetails=Single.zip(
getCustomerUserProfile(forceRefresh).subscribeOn(Schedulers.io()),
getAccountId(forceRefresh).subscribeOn(Schedulers.io()),
getCustomerAccountDetails(false).subscribeOn(Schedulers.io()),
getCustomer(forceRefresh).subscribeOn(Schedulers.io()),新函数4(){
@凌驾
公共CustomerDetails应用(@NonNull CustomerType CustomerType,
@非空CustomerAccountId CustomerAccountId,
@非空CustomerAccountVO CustomerAccountVO,
@非Null客户)引发异常{
返回CustomerDetails.builder().customerType(customerType).customerAccountVO
(customerAccountVO).客户(customer).customerAccountId(customerAccountId)。
build();
}
});
返回客户详细信息;
}
//==========
每个客户请求都是独立的,所以我认为应该以sperate线程的形式执行,并压缩最终结果/
单个updateCustomerDetails(@Nonnull boolean secure,int secureRequestCode,@Nonnull JSONObject customerContact);
//演示者实现:这个实现没有像我上面期望的那样工作,有人能帮我纠正一下吗,
public void doLoginHandler(@NonNull字符串用户名,@NonNull字符串密码){
选中ViewAttached();
getMvpView().showLoadingIndicator();
添加订阅(
apiService.doLogin2(用户名、密码)
.subscribeOn(Schedulers.io())
.flatMap(新函数(){
@凌驾
公共单源应用(布尔aBoolean)引发异常{
if(阿布奥兰语){
.flatMapCompletable(new Function<CustomerDetails, CompletableSource>() {
                    @Override
                    public CompletableSource apply(CustomerDetails customerDetails) throws Exception {
                        if(customerDetails.isErrorOccurred()){
                            return apiService.doLogout();
                        }
                        return Completable.complete();
                    }
                })
loginObservable.flatMap { authCredentials -> { 
if (authCredentials.isValid())
    return getCustomerTypeObservable(authCredentials)
else
    return Single.error(InvalidCredentialsException("message goes here (optional)"))
}}.flatMap { type -> {
    if (type == 0)
        return Single.error(ProfileRestrictedException("different message maybe?"))
    else
        return getCustomerDetailsZippedObservable(type)
}}
/* ..etc */
myObservable.subscribe( {
/* Handle success*/
}, { exception ->
    when(exception) {
        is InvalidCredentialsException -> mvpView.showError(message)
        is ProfileRestrictedException -> { 
            mvpView.showError(message)
            logout()
        }
        else -> /* Handle an exception that is not listed above */
    }
.flatMapCompletable { customerDetails -> {
    if(customerDetails.isErrorOccurred()){
        return apiService.doLogout()
                .then(Completable.error(LoginFailedException("Message"))) /* This will guarantee the stream terminates with the required error type after logout is successful */
    } else {
        return Completable.complete()
    }
}}