Angular response interceptor does not work for RestAngular - angularjs

I have the following code:
!(function(window, angular){
'use strict';
angular.module('interceptor', ['settings']).
config(function($httpProvider, $injector){
var $http,
interceptor = ['$q', '$injector', function ($q, $injector) {
var error;
function success(response) {
var $http = $http || $injector.get('$http');
var $timeout = $timeout || $injector.get('$timeout');
var $rootScope = $rootScope || $injector.get('$rootScope');
var LOADER_CONF = LOADER_CONF || $injector.get('LOADER_CONF');
if($http.pendingRequests.length < 1) {
$timeout(function(){
if($http.pendingRequests.length < 1){
$rootScope._loading = false;
}
}, LOADER_CONF.SUSPEND_ON);
}
else{
$timeout(function(){
if($http.pendingRequests.length > 0){
$rootScope._loading = true;
}
}, LOADER_CONF.SUSPEND_OFF);
}
return response;
}
function error(response) {
var $state = $state || $injector.get("$state");
var $timeout = $timeout || $injector.get('$timeout');
var $rootScope = $rootScope || $injector.get('$rootScope');
var LOADER_CONF = LOADER_CONF || $injector.get('LOADER_CONF');
$timeout(function(){ $rootScope._loading = false, LOADER_CONF.SUSPEND_OFF});
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
});
})(window, window.angular);
a basic interceptor to toggle a spinner.
The interceptor works just fine for http requests that fetch partials, and internal angular stuff, but does not work for RestAngular requests (which is a wrapper for $http).
Any help is much appreciated.

it's possible Restangular interception isn't working because of promises. try using $q like so:
var interceptor = ['$rootScope', '$q', function ($rootScope, $q) {
return {
'request': function(config) {
console.log("request successful");
return config || $q.when(config);
},
'requestError': function(rejection) {
console.log("request error");
return $q.reject(rejection);
},
'response': function(response) {
console.log("response successful");
return response || $q.when(response);
},
'responseError' : function(rejection) {
console.log(rejection.status+" something bad happened");
$rootScope.errors = ["Unable to connect to server "+rejection.config.url];
return $q.reject(rejection);
}
};
}];

Related

Notification alert service not working in controller getting typeError: Cannot read property 'alerts' of undefined

I have capturing the application response error ,while capturing the error, i am getting the error.
In Interceptor ,according to the response code , have assign the rootscope broadcast and show the alert message in controller.
Here $rootScope.$broadcast('loginRequired'); is assigning in interceptor and while capturing in service response inside controller.
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
interceptor.
var interceptor = function($q, alerts, $rootScope, $timeout, $location) {
return {
request: function(config) {
console.log(config);
return config;
},
response: function(response) {
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status == 500) {
$location.url('/ho');
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return $q.reject(rejection);
}
console.log(rejection.status);
return $q.reject(rejection);
}
}
};
$httpProvider.interceptors.push(interceptor);
alertManagerfactory
var alertsManager = function() {
return {
alerts: {},
addAlert: function(message, type) {
this.alerts[type] = this.alerts[type] || [];
this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in this.alerts) {
delete this.alerts[x];
}
}
};
};
alertsManager.$inject = [];
In controller :
var LoginController = function($scope, $rootScope, alerts, alertsManager) {
$scope.alerts = alertsManager.alerts;
// getting error in this line
//getting typeError: Cannot read property 'alerts' of undefined
LoginService.AfterLogin(username, password)
.then(function(response) {}, function(status) {
console.log("Error" + status);
if (status === 500) {
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
}
});
};
LoginController.$inject = ['$scope', '$rootScope', 'alerts', 'alertsManager'];
In controller view.
<div ng-repeat="alert in alerts" ng-class="'alert-' + (alert.type || 'warning')" close="closeAlert($index)">{{alert.msg}}</div>
"this" keyword in your addAlert "method" is actually referencing anonymous function you have assigned to addAlert prop.
There are couple of ways to deal with this. For example creating variable that holds your object.
var alertsManager = function() {
var $this = {
alerts: {},
addAlert: function(message, type) {
$this.alerts[type] = $this.alerts[type] || [];
$this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in $this.alerts) {
delete $this.alerts[x];
}
}
};
return $this;
};
alertsManager.$inject = [];

AngularJS interceptor not returning on success

I'm trying to add a popup with retry option as follows so that the user can click on that when lose the connection during a HTTP call.
angular.module('app.services', [])
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
if (response.status === 0) {
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
}
}
};
}])
It is receiving the response from the http call, but not returning the response to the calling point. In the same way I tried the following code,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
if (response.status === 0) {
var $http = $injector.get('$http');
return $http(response.config);
}
return $q.reject(response);
}
};
}])
But this one is returning the response properly to the calling point when we get the connection back. I'm not sure where I'm going wrong in the first code.
Any help/idea would be appreciated.
You should return confirmPopup.then() call.
Do like this:
return confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
Example of chaining:
var promise = confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
promise.then(function(success){
//HTTP SUCCESS
}, function(error){
//HTTP ERROR OR REJECT RESPONSE
});
Based on Patrick Kelleter's answer, I framed this working solution,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
var $ionicLoading = $injector.get('$ionicLoading');
$ionicLoading.hide();
if (response.status === 0) {
var userInputDefer = $q.defer();
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available',
okText: 'Retry'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
userInputDefer.resolve($http(response.config));
} else {
userInputDefer.reject($q.reject(response));
}
});
return userInputDefer.promise;
}
}
};
}]);
Edit:
Just for future reference for someone, for using the above HTTP interceptor, you have to include the factory in config as follows,
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpResponseErrorInterceptor');
}]);
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
here is the problem. you are returning stuff ($http / $q) in the asynchronous callback of the confirmPopup.
the confirmPopup is async and you define a callback via ".then".
whatever you are returning there will not reach your calling point. it is the return value of the callback. which probably will not land anywhere (depending on the implementation of confirmPopup, but i doubt that it expects you to return anything there)
you will have to use your own promise and return it synchronously at the end of your callback

Angular and Jasmine: How to test requestError / rejection in HTTP interceptor?

How would you test this requestError method?
angular.module('myApp').factory 'HTTPInterceptor', [
'$rootScope'
'$q'
'$window'
'LocalStorageService'
'$injector'
($rootScope, $q, $window, $injector) ->
{
request: (config) ->
config.headers = config.headers or {}
// do stuff with config then
config # Return Config
requestError: (rejection) ->
q.reject(rejection) # Return the promise rejection
...
Hey its been long since you post this question but I think I have solution for this question.Last describe is for requestError method in intreceptors. Here is example of how I test my httpInterceptor:
TEST
/* jshint undef:false*/
(function() {
'use strict';
describe('httpInterceptor', function() {
var httpInterceptor, modal, state, q, actualOptions, rootScope, scope, mockAuthenticationService, notification;
beforeEach(module('app'));
beforeEach(inject(function(_httpInterceptor_, $q, $uibModal, $state, $rootScope, _AuthenticationService_, _Notification_) {
httpInterceptor = _httpInterceptor_;
rootScope = $rootScope;
scope = rootScope.$new();
q = $q;
state = $state;
mockAuthenticationService = _AuthenticationService_;
notification = _Notification_;
function FakeModal(){
this.resultDeferred = $q.defer();
this.result = this.resultDeferred.promise;
}
FakeModal.prototype.open = function(options){
actualOptions = options;
return this; };
FakeModal.prototype.close = function (item) {
this.resultDeferred.resolve(item);
rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};
FakeModal.prototype.dismiss = function (item) {
this.resultDeferred.reject(item);
rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};
modal = new FakeModal();
}));
describe('httpInterceptor', function() {
beforeEach(function () {
var rejection = { status: 400 , data: {exception: "class org.usda.fns.memsng.exceptions.ReviewTimeoutException"}};
httpInterceptor.responseError(rejection);
});
it('with 400 error status and open idle modal', function () {
rootScope.$apply();
modal.close('close');
});
});
describe('httpInterceptor', function() {
it('with 400 error status and open notification', function () {
var rejection = { status: 400 , data: {exception: "test"}};
httpInterceptor.responseError(rejection);
});
});
describe('httpInterceptor', function() {
beforeEach(function () {
spyOn(state, 'go');
var rejection = { status: 403 };
httpInterceptor.responseError(rejection);
});
it('with 403 error status and go to unauthorized page', function () {
var expectedState = 'root.unauthorized';
expect(state.go).toHaveBeenCalledWith(expectedState);
});
});
describe('httpInterceptor', function() {
beforeEach(function () {
spyOn(state, 'go');
});
it('with requestError method', function () {
var rejection = { status: 403 };
httpInterceptor.requestError(rejection);
});
});
});
})();
Actual Interceptor
function httpInterceptor($q, $log, $injector) {
return {
request: function(config) {
return config;
},
requestError: function(rejection) {
$log.debug(rejection);
return $q.reject(rejection);
},
response: function(response) {
$log.debug('response: ', response);
return response;
},
responseError: function(rejection) {
$log.debug(rejection);
var state = $injector.get('$state');
if (rejection.status === 403) {
state.go('root.unauthorized');
}
return $q.reject(rejection);
}
};
}

AngularJS Interceptor to Redirect

ExpressJS is sending the following response...
res.send('ItemUploaded');
I'm trying to get AngularJS to see this response via an Interceptor and perform a redirect. Does anyone have sample code where Angular catches a server response (such as my "ItemUploaded") and performs a redirect to a partial (via $location)?
This works fine. I have used it in my application.
var interceptor = function ($q, $location) {
return {
request: function (config) {//req
console.log(config);
return config;
},
response: function (result) {//res
console.log('Repos:');
console.log(result.status);
return result;
},
responseError: function (rejection) {//error
console.log('Failed with', rejection.status, 'status');
if (rejection.status == 403) {
$location.url('/dashboard');
}
return $q.reject(rejection);
}
}
};
module.config(function ($httpProvider) {
$httpProvider.interceptors.push(interceptor);
});
Here is the factory for the interceptor:
.factory('InterceptorService',['$q', '$location', function( $q, $location, $http){
var InterceptorServiceFactory = {};
var _request = function(config){
//success logic here
return config;
}
var _responseError = function(rejection) {
//error here. for example server respond with 401
return $q.reject(rejection);
}
InterceptorServiceFactory.request = _request;
InterceptorServiceFactory.responseError = _responseError;
return InterceptorServiceFactory;
}]);
then register the interceptor:
.config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push('InterceptorService');
}]);
Every request coming will be passed here.
You can implement a interceptor factory which will redirect if it gets a matching result.
angular
.module('app')
.factory("httpinterceptor", ["$location",
function(location) {
return {
'response': function(response) {
if (response.data === "ItemUploaded") {
location.path("/ItemUploaded")
}
}
}
}
]);

AngularJS - Calling service in config

I am trying to call AuthenticationService.logout() on a 401 http error. However, I can't make it work. I suppose I can't inject a service to the config, but how can I achieve this then?
myApp.config(['$httpProvider', 'AuthenticationService', function ($httpProvider, AuthenticationService) {
var interceptor = ['$rootScope','$q', function(scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
}
/* LOGOUT HERE */
AuthenticationService.logout();
return deferred.promise;
}
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}]);
What am I doing wrong, and how can I fix it?

Resources