Handle errors with Zip operator, Rxjava2 and Retrofit - android

So I have these two parallel calls using zip operator. I am making two network calls. I have the following questions:
How can I handle the individual errors correctly
If the first call fails I want to be able to exit the session but if the second network call fails I want to allow the user to still go through the session. I am seeing a 404 in my second network call in the zip and the entire chain fails with an error. I want it to be able to handle success and failure
valid session
response 1: success
response 2: failure
invalid session
response 1: failure
response 2: success
invalid session
both endpoints fail
Single.zip(
api1.getData().doOnError {
// handle error : exit right away
},
api2.getData().doOnError {
// handle error: Set profile data to be empty but when user tries to see the profile information show error at a later point in time based oaths response
// got 404
},
{ response1: String, response2: CustomObject ->
Pair(response1, response2)
}
)
.subscribeOn(Schedulers.io())
.subscribe(
{
handleResponse1(it.first)
handleRespone2(it.second)
},
{
Timber.d("it : $it")
// api1 use success response: is it even possibel to get that in the iterator
// api 2 throwing 404 here
}
)

From your question, I see that you want to continue even if one of the API fails without failing the whole chain. This can be done in the following way
If you want to exit on the first API call no need to handle any Error there.(You will get an error in throwable)
Whenever the second API fails use onErrorReturnItem to return some empty response
Single.zip(
api1.getData().subscribeOn(Schedulers.io()),
api2.getData().subscribeOn(Schedulers.io())
.onErrorReturnItem(new Response())
.......
The new Response() here is just an empty object of the response of type that you were expecting. Even if the second API fails here you will get whatever you are returing
If the first API fails here you will get a callback in Throwable or you can continue to handle error in doOnError

Related

Catch no network error in Angular HttpClient

How to catch a no network (no internet connection) error from Rxjs Observable using Angular HttpClient
RXJS Catch error code
.catch((err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
}
// ...optionally return a default fallback value so app can continue (pick one)
// which could be a default value
// return Observable.of<any>({my: "default value..."});
// or simply an empty observable
return Observable.empty<T>();
});
Same angular error code
I'm trying to catch errors when the user has no network and is trying to post off an API request. In the code above, it says that err.error instanceof Error would catch any network errors but it doesn't.
Using both Ios and Android simulators, whenever there's no network, I would get a error code of 0 and message, Unknown Error. I can just check when error code 0, then return a no internet connection message but I can never be certain it's actually a network error.
Is there any method to guarantee the error is a no internet connection issue?
Sure I could check for a network before getting or posting api requests, but then all my pages with api requests need to continuously monitor for a network connection.
You could use navigator.onLine as an additional check if you get an error code 0.
Though I'm not sure how reliable this variable is.

Retrofit 2.5.0 not executing request after third time

I have the following code,
#Singleton
class LoginHandler {
private Observable<Response<User>> loginObservable;
private void attemptLogin(LoginRequest loginRequest) {
LoginSubscriber loginSubscriber = new LoginSubscriber();
loginObservable = api.login(loginRequest);
loginObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(loginSubscriber);
}
}
with
#POST(PATH_AUTH)
Observable<Response<User>> login(#Body LoginRequest loginRequest);
This gets executed each time the user wants to login from a deep link.
I can log in and log out two times, but on the third time, the request just does not get executed. I also checked with the Charles Proxy tool, if the request is actually sent, but there was nothing to see.
Logging the loginObservable object shows that each api.login() call returns a different Observable object, which is correct for the subscription to be executed. Logging at doOnSubscribe(() -> ...) is also executed, just not the request itself.
I've browsed through SO and the only solution I found was to add a timeout(...) so that the user is able to get back to the login mask, once the timeout strikes. However when retrying to log in, the problem is still present.
So my question is, how could I debug this and find out, what is happening under the hood of retrofit and why is the request not executed after the third attempt?
Thank you very much in advance

Strange Laravel/Stripe/Cartalyst bug where a charge randomly runs twice using the same Stripe token

I've created an Android mobile app which has integrated Stripe to accept card payments. In my Android app, I send to my Laravel API, a bunch of parameters, one of which is a Stripe token.
9 times out of 10, my server handles this as expected. It will create a charge object using Stripe-Cartalyst and create a charge, assuming no exceptions are thrown (such as bad card details, or a server error), my api will then proceed to handle the payment as a success and do some inserts into a DB, after which it will return a 201 HTTP status code back to the client, where I then handle this on the Android side.
The bug (or something I'm doing incorrectly) I've encountered though, happens randomly. Occasionally my API will throw an uncaught exception from Stripe explaining a token (encrypted Stripe card details), can only be used once. I've done some debugging on the Android side and can verify I'm only making 1 HTTP request where I send the token, and have done some Logging on my Laravel API, and have found some strange goings on.
What I've found
On the API, I printed some key moments of the process to a log file.
Print the token variable.
When entering the try block to create the charge object.
If 'Delivery' is selected on the Android app (probably a pointless log here).
Checking some items (this is an array passed from the Android app.
At the end of the charge, before returning the 201 back to the client, print a success message of some kind.
I then proceeded to recreate the bug, after about 20/30 orders it happened, and this is the process that occurred:
First, on the Android app, the following token was sent (and printed out to the console);
tok_1DiK0uKIdjSiVG8mn8CV2iim
After checking, this HTTP request was only triggered once, so I don't think it's an Android issue.
Next, on the API log file, the following occurred:
2018-12-17 11:11:56] local.DEBUG: Token: tok_1DiK0uKIdjSiVG8mn8CV2iim
[2018-12-17 11:11:56] local.DEBUG: Creating Stripe Charge
[2018-12-17 11:11:59] local.DEBUG: Token: tok_1DiK0uKIdjSiVG8mn8CV2iim
[2018-12-17 11:11:59] local.DEBUG: Creating Stripe Charge
[2018-12-17 11:12:00] local.ERROR: There is currently another
in-progress request using this Stripe token (that probably means you
clicked twice, and the other charge is still going through):
tok_1DiK0uKIdjSiVG8mn8CV2iim. This token can not be used again if that
charge is successful.' {"exception":"[object]
(Cartalyst\Stripe\Exception\MissingParameterException(code: 400):
There is currently another in-progress request using this Stripe token
(that probably means you clicked twice, and the other charge is still
going through): tok_1DiK0uKIdjSiVG8mn8CV2iim. This token can not be
used again if that charge is successful.' at
/home/rbfs6nkk73qi/api/prototype/vendor/cartalyst/stripe/src/Exception/Handler.php:123)
[stacktrace]
[2018-12-17 11:12:00] local.DEBUG: Delivery order
[2018-12-17 11:12:00] local.DEBUG: Checking single items
[2018-12-17 11:12:00] local.DEBUG: Card delivery - Order placed via
Android V0.5 app
This shows that from the steps I listed 1-5, the API did: 1,2,1,2,error,3,4,5.
I'm at a complete loss as to why my API would randomly run twice. I've pasted the relevant parts of my API below, to see if there is something obviously wrong that I'm doing. Appreciate any help on this.
One last thing I've tried: I've tried setting the $token variable to null directly after creating the $stripe charge object, which makes me wonder if this is some Cartalyst bug.
if($card) {
$token = $request->token; //Stripe token
Log::debug("Token: ".$token); //Step 1
try {
Log::debug("Creating Stripe Charge"); //Step 2
$stripe->charges()->create([
'currency' => $currency,
'amount' => $amount,
'source' => $token
]);
if(strcmp($delivery,"Delivery") == 0) {
Log::debug("Delivery order"); //Step 3
if($singleItems != null) {
Log::debug("Checking single items"); //Step 4
foreach($singleItems as $key => $value) {
$singleItem = DB::table('items')
->select('name','category','price')
->where('item_id', '=', $key)
->get();
foreach($singleItem as $item) {
DB::table('single_order_items')->insert(['order_number' => $id, 'item'=>$key, 'quantity'=>$value, 'name'=>$item->name, 'category'=>$item->category, 'price'=>$item->price]);
}
}
}
Log::debug("Card delivery - ".$description); //Step 5
return response("Order placed successfully", 201)
->header('Content-Type', 'text/plain');
}
} catch(\Cartalyst\Stripe\Exception\BadRequestException $e) {
//This exception will be thrown when the data sent through the request is mal formed.
$message = $e->getMessage();
Log::debug($message);
return response($message, 306)
->header('Content-Type', 'text/plain');
} catch(\Cartalyst\Stripe\Exception\UnauthorizedException $e) {
//This exception will be thrown if your Stripe API Key is incorrect.
$message = $e->getMessage();
Log::debug($message);
return response($message, 307)
->header('Content-Type', 'text/plain');
} catch(\Cartalyst\Stripe\Exception\InvalidRequestException $e) {
//This exception will be thrown whenever the request fails for some reason.
$message = $e->getMessage();
Log::debug($message);
return response($message, 308)
->header('Content-Type', 'text/plain');
} catch(\Cartalyst\Stripe\Exception\NotFoundException $e) {
//This exception will be thrown whenever a request results on a 404.
$message = $e->getMessage();
Log::debug($message);
return response($message, 309)
->header('Content-Type', 'text/plain');
} catch(\Cartalyst\Stripe\Exception\CardErrorException $e) {
//This exception will be thrown whenever the credit card is invalid.
$message = $e->getMessage();
Log::debug($message);
return response($message, 310)
->header('Content-Type', 'text/plain');
} catch(\Cartalyst\Stripe\Exception\ServerErrorException $e) {
//This exception will be thrown whenever Stripe does something wrong.
$message = $e->getMessage();
Log::debug($message);
return response($message, 311)
->header('Content-Type', 'text/plain');
}
}
After further debugging, a solution was found. It turns out the HTTP library I'm using on the Android side called Volley was sending the request more than once.
Setting the Volley request to have 0 retries seems to have solved the issue:
MyStringRequest.setRetryPolicy(new DefaultRetryPolicy(0,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

Robospice caching when exception occured

I wan't to achieve next.
Depends on json content deside put or not to put data to Robospice Cache.
Sometimes data returned from the server is not valid. For example our authorization token goes off time. So we shouldn't cache this response.
But i can't find API which can help me to solve this trouble.
Here is how i am using requests now:
getSpiceManager().execute(getRequestCreator().getAllCategories(), getRequestCreator().getLastCacheKey(),
DurationInMillis.ONE_MINUTE * 120, new JSONCategoryListener(mCategories));
So the actual response is normal (status 200), but json content is telling me about exception.
So, what you want to get? It is right behavior for server. Server returns 200, means that request is successful. But it not guarantee, that wasn't some internal error of 'business logic' on server, such as not valid data or anything else.
EDITED
May be you can use your custom error handler:
class MyErrorHandler implements ErrorHandler {
#Override public Throwable handleError(RetrofitError cause) {
//check response on errors
}
}
And in createRestAdapterBuilder():
new RestAdapter.Builder()..setErrorHandler(new MyErrorHandler());
EDITED 2
You can implement in your robospice service method putDataInCache(Object cacheKey, T data), and in your listener check errors in content, and if no error then add it to cache, or remove it from cache

Android Browser not consistently return json from ajax call

I'm making an ajax call that repeatedly calls an api for json data. I've never had it fail with other browsers, but something weird is happening to the response within the Android Browser. I put a console log using weinre that catches the returned data. Can anyone make sense of it and how to handle it?
Screenshot:
Everything is running along smoothly until the last one causes an error like this: Uncaught TypeError: Cannot read property 'Requests' of undefined
Why is it not returning like the previous json return from the ajax calls?
I couldn't find anyway to handle this other than wrap the whole result in a try catch. If it does catch something then it just tries again to get the batch and has thus far always been successful on the second attempt.
try {
//ajax returned result
} catch (error) {
console.error(error);
window.setTimeout(function() {
getBatch();
}, 2000);
}

Categories

Resources