How to handle loss of connection in Angular2 with RXJS HTTP when polling - http

I have the following code (simplified for this post) - assume an initial call to onStart().
Running this works fine. If I lose the internet connection I get the net::ERR_INTERNET_DISCONNECTED error (as expected) but the polling stops.
Clearly I am not handling any errors here as that is where I'm getting stuck. I'm not clear where I handle those errors and how? Do I need to call startPolling() again?
I need the polling to continue even if there is no internet connection, so that on re-connection data is updated. Any advice please?
onStart() {
this.startPolling().subscribe(data => {
// do something with the data
});
}
startPolling(): Observable<any> {
return Observable
.interval(10000)
.flatMap(() => this.getData());
}
getData() {
var url = `http://someurl.com/api`;
return this.http.get(url)
.map(response => {
return response.json();
});
}
Thanks in advance.

If you know the error happens because of this.http.get(url) then you can add catch() operator that lets you subscribe to another Observable instead of the source Observable that sent an error notification.
getData() {
var url = `http://someurl.com/api`;
return this.http.get(url)
.catch(err => Observable.empty())
.map(response => {
return response.json();
});
}
This will simply ignore the error and won't emit anything.

Related

Where is the best place to modify backend response with redux-saga?

I have a function that prepares the errors from backend to be easy for displaying in the components - it's named prepareErrorMessages. It accepts the response from the backend and some default error message.
So - in the saga I have this:
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS
});
}
} catch (err) {
yield put({
type: UPDATE_FAILURE,
payload: prepareErrorMessages(err, 'Failed to update. Please, try again.')
});
}
}
So - am I wrong to modify the errors from the backend here?
Or is it better to do this in the reducer?
case UPDATE_FAILURE:
nextState = {
...state,
loading: false,
errors: prepareErrorMessages(payload, 'Failed to update. Please, try again.'),
};
break;
And also - why is it better to update there?
Personally, I think its right to do it in the reducer.
That is where you handle the responses. Action creators should only set the payload which could be some static data or a promise.
Dont see why you cannot transform/modify the received data there.
Personally, I would prefer to have it in the saga because I think it is the right place of handling this kind of logic.
I prefer my reducers to only be responsible for changing state, not for data transformation.
But it is my personal opinion.
We are using a Transformer for transforming the response getting from the Api. Transformer is the function which takes the input and provide the desired output. Designing the transformer makes the code clean and easy to test.
For example :-
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
// after getting the response from the api pass through the transformer.
const transformedResponse =apiTransformer(action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS,
data: trasnformedResponse
});
}
} catch (error) {
yield put({
type: UPDATE_FAILURE,
error: error)
});
}
}
const apiTransformer = function(apiResponse) {
// implement the logic. This function returns the transformed Response
}
Using this you can move reducer free from the error. Makes the code testable and making mocks easy.
For global backend Errors make a global errorHandler using Redux Middleware, like this
const errorTracking = store => next => action => {
if (/_FAILURE$/.test(action.type)) {
const errorCode = parseInt(
_.get(action, ['error', 'response', 'status'])
)
// this was for my use case
if (errorCode === 403) {
// launch an Global error handler action
return next(ErrorHandlerAction())
} else return next(action)
}
return next(action)
}
While for not genric error You can implement HOC wrap it around the component for visualisation. Thus you can have global implementation for the errors.

Google:assistance the final response did not set

we have Google Assistance project, that is working fine, for every intent,
now i want to get dynamic data from web service and return, for that, I am using request module, but its giving error
the final response did not set
below is my code
app.intent('doctor_list', (conv, {doctor}) => {
Request.get("url", (error, response, body) => {
if(error) {
con.ask('data return')
}
con.ask('err')
});
})
You aren't clear in what your Request object is, but I suspect the problem is that you're not returning a Promise object from your call. Network calls are handled asynchronously in node.js, and when you make an async call, you need to return a Promise object.
The easiest way to handle this is using the request-promise-native package. Your code might look something like this:
const Request = require('request-promise-native');
app.intent('doctor_list', (conv,{doctor}) => {
return Request.get('url')
.then( body => {
conv.ask( 'data return' );
})
.catch( err => {
console.log( err );
conv.close( 'error' );
});
});

restarting a queue of API requests if a token refresh happened

I'm having a hard time wrapping my brain around this pattern I am trying to implement so I'm hoping the stack overflow community might be able to help me work through a solution to this.
Currently I use redux-thunk along with superagent to handle calls to me API and syncing it all up with redux
An example of this might look like
export const getUser = (id) => {
return (dispatch) => {
const deferred = new Promise((resolve, reject) => {
const call = () => {
API.get(`/users/${id}`)
.then((response) => response.body)
.then((response) => {
if (response.message === 'User found') {
serializeUser(response.data).then((response) => {
resolve(response);
});
} else {
reject('not found');
}
}).catch((err) => {
handleCatch(err, dispatch).then(call).catch(reject)
});
}
call()
});
return deferred;
};
};
In the case where the server comes back with a 200 and some data I continue on with putting the data into the store and rendering to the page or whatever my application does.
In the case I receive an error I have attempted to write a function that will intercept those and determine if it should show an error on page or in the case of a 401 from our API, attempt a token refresh and then try to recall the method...
import { refreshToken } from '../actions/authentication';
export default (err, dispatch) => {
const deferred = new Promise((resolve, reject) => {
if (err.status === 401) {
dispatch(refreshToken()).then(resolve).catch(reject)
} else {
reject(err);
}
})
return deferred;
};
This works, however, I have to add this to each call, and it doesn't account for concurrent calls that should not attempt to call if there is a refresh in progress.
I've seen some things in my research on this topic that maybe redux-saga could work but I haven't been able to wrap my brain around how I might make this work
Basically, I need something like a queue that all my API requests will go into that is maybe debounced so any concurrent requests will just be pushed to the end and once a timeout ends the calls get stacked up, when the first call gets a 401 it pauses the queue until the token refresh either comes back successful, in which case it continues the queue, or with a failure, in which case it cancels all future requests from the queue and sends the user back to a login page
The thing I would be worried about here is if the first call in the stack takes a long time, I don't want the other calls to then have to wait a long time because it will increase the perceived loading time to the user
Is there a better way to handle keeping tokens refreshed?

rxjs - How to retry after catching and processing an error with emitting something

I'm using rxjs v5.4.3, redux-observable v0.16.0.
in my application, I'd like to achieve below:
an user has auth token, and refresh token to regenerate auth token.
the user requests with auth token. (by emitting REQUEST action)
if it failed, request regenerating auth token with refresh token.
if refreshed, emit TOKEN_REFRESHED action to update auth token, and do not emit REQUEST_FAILURE.
if refreshing failed, emit REQUEST_FAILURE
after refreshing(and updating auth token reducer), retry requesting using the refreshed auth token.
if request succeeded, emit REQUEST_SUCCESS, and if failed, emit REQUEST_FAILURE.
I'd like to achieve like:
const fetchEpic = (action$: ActionsObservable<Action>, store: Store<IRootState>) => action$
.ofAction(actions.fetchPost)
.mergeMap(({ payload: { postId } })) => {
const { authToken, refreshToken } = store.getState().auth;
return api.fetchPost({ postId, authToken }) // this returns Observable<ResponseJSON>
.map(res => actions.fetchSuccess({ res })) // if success, just emit success-action with the response
.catch(err => {
if (isAuthTokenExpiredError(err) {
return api.reAuthenticate({ refreshToken })
.map(res => actions.refreshTokenSuccess({ authToken: res.authToken });
.catch(actions.fetchFailure({ err }))
// and retry fetchPost after re-authenticate!
}
return Observable.of(actions.fetchFailure({ err }))
})
}
is there any solution?
There are many ways to do it, but I would recommend splitting off the reauthentication into its own epic to make it easier to maintain/test/reuse.
Here's what that might look like:
const reAuthenticateEpic = (action$, store) =>
action$.ofType(actions.reAuthenticate)
.switchMap(() => {
const { refreshToken } = store.getState().auth;
return api.reAuthenticate({ refreshToken })
.map(res => actions.refreshTokenSuccess({ authToken: res.authToken }))
.catch(err => Observable.of(
actions.refreshTokenFailure({ err })
));
});
We'll also want to use something like Observable.defer so that each time we retry, we look up the latest version of the authToken:
Observable.defer(() => {
const { authToken } = store.getState().auth;
return api.fetchPost({ postId, authToken });
})
When we catch errors in fetchEpic and detect isAuthTokenExpiredError we return an Observable chain that:
Starts listening for a single refreshTokenSuccess, signalling we can retry
Just in case the reauthing itself fails, we listen for it with .takeUntil(action$.ofType(refreshTokenFailure)) so that we aren't waiting around forever--you might want to handle this case differently, your call.
mergeMap it to the original source, which is the second argument of the catch callback. The "source" is the Observable chain before the catch, and since Observables are lazy, when we receive the refreshTokenSuccess action it it will resubscribe to that chain again, effectively be a "retrying"
Merge the above chain with an Observable of an reAuthenticate action. This is used to kick off the actual reauth.
To summarize: the Observable chain we return from catch will first starting listening for refreshTokenSuccess, then it emits reAuthenticate, then when (and if) we receive refreshTokenSuccess we will then "retry" the source, our api.fetchPost() chain above the catch that we wrapped in an Observable.defer. If refreshTokenFailure is emitted before we receive our refreshTokenSuccess, we give up entirely.
const fetchEpic = (action$, store) =>
action$.ofType(actions.fetchPost)
.mergeMap(({ payload: { postId } })) =>
Observable.defer(() => {
const { authToken } = store.getState().auth;
return api.fetchPost({ postId, authToken });
})
.map(res => actions.fetchSuccess({ res }))
.catch((err, source) => {
if (isAuthTokenExpiredError(err)) {
// Start listening for refreshTokenSuccess, then kick off the reauth
return action$.ofType(actions.refreshTokenSuccess)
.takeUntil(action$.ofType(refreshTokenFailure))
.take(1)
.mergeMapTo(source) // same as .mergeMap(() => source)
.merge(
Observable.of(action.reAuthenticate())
);
} else {
return Observable.of(actions.fetchFailure({ err }));
}
});
);
These examples are untested, so I may have some minor issues but you hopefully get the gist. There's also probably a more elegant way to do this, but this will at least unblock you. (Others are more than welcome to edit this answer if they can decrease the complexity)
Side notes
This creates the slight potential for infinite retries, which can cause nasty issues both in the person's browser or your servers. It might be a good idea to only retry a set number of times, and/or put some sort of delay in between your retries. In practice this might not be worth worrying about, you'll know best.
You (or someone else reading this later) may be tempted to use .startWith(action.reAuthenticate()) instead of the merge, but be mindful that a startWith is just shorthand for a concat, not a merge, which means it would synchronously emit the action before we have started to listen for a success one. Usually that isn't an issue since http requests are async, but it's caused people bugs before.

RxJS wait for second observable then retry original observable on error - TypeScript/Angular 2

I am fairly new to Angular 2, TypeScript and RxJS and I am creating a simple application that leverages the Salesforce Ajax Toolkit connections library.
I am trying to write a handler to catch when a token has expired any time a method from the connections library is called. I have created a service that essentially wraps the connections library to use observables. For example if we look at the insert function I have created my own wrapper function:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => {
// This does not work yet
if (result.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
this.refreshToken();
}
else {
observer.error(result);
}
}
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
});
}
I have another function that refreshes the access token:
public refreshToken(): void {
this.loginService.login().subscribe(
response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
},
error => {
}
);
}
I essentially want the original insert function to wait for refreshToken to complete. If it is successful I want to retry the same insert again, otherwise I want the original insert observable to call observer.error.
I've looked into retry and retryWhen, however I haven't been able to figure out how to implement it to wait for the refreshToken() function to complete. Any guidance or advice on this matter would be greatly appreciated. Thank you in advance.
The catch operator accepts a function which processes an error and the source Observable. This means that if you catch an error you can determine whether you want to resubscribe to the original source in the catch block:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => observer.error(result);
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
}).catch((err, source) => {
if (err.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
//This waits for the refresh to complete and then resubscribes
//to the source
//If the refresh errors then it will skip the resubscribe
return this.refreshToken().flatMapTo(source);
}
//Non-authentication error
return Observable.throw(err);
});
}
Then make your refreshToken function into something like so:
public refreshToken(): Observable<any> {
return this.loginService.login()
.tap(response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
});
}

Resources