ng mock e2e Unexpected Request - angularjs

I'm using the ngMockE2E to mock the httpBackend while developing the UI in Angular JS. The App runs on a Grizzly-Server with a backend which is provided by a virtual machine. Now when I go on the Website the Console logs the Error:
Unexpected request: GET /api/info
No more request expected
In my case I only want to mock the data of one database. Additional to that I want to turn the mock ON/OFF on the fly via a button. This was working until the Error comes up.
For that case I've written the following:
$httpBackend.whenPOST(function (url) {
if (!mockup) {
return false;
}
var target_url = (someUrl);
return target_url === url;
}).respond(function (method, url, data) {
return [200, [someData], {}, 'mockupData'];
}
);
Additional to that I've added the following to pass all the other requests:
// pass the rest of the queries
$httpBackend.whenGET(/.*/).passThrough();
$httpBackend.whenPOST(/.*/).passThrough();
$httpBackend.whenPUT(/.*/).passThrough();
$httpBackend.whenDELETE(/.*/).passThrough();

So for all of you who face the same problem. I found a workaround for the issue. But I'm sure there should be another better solution.
For that I used an Interceptor which is also provided by angular. Here the api
So I wrote this:
app.factory('httpInterceptor', function () {
return {
'response': function (response) {
// therefore you can enable the mockup with a switch or a button
if (!mockup) {
return response;
}
try {
// in the config object is the requested url
if (response.config.url !== someUrl) {
// don't intercept; return response unchanged
return response;
}
// found an url which should be mocked
// generate or modify the data
var generatedData = generateSomeData();
response.data.push(generatedData)
return response;
} catch (TypeError) {
// don't intercept
return response;
}
}
}
}
);
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);

Related

AngularJs http request priorities and http interceptors

I have been looking at an application I made a while back and there is a particular page where the details are being loaded last. Because of this, it seems to be queuing the request (there are more than 6 others before it) and that is causing the page to be slow.
I figured I could find a solution to prioritize these requests and I found this:
How to prioritize requests in angular $http service?
So I created my version of it and added it to my interceptors:
// Add our auth interceptor to handle authenticated requests
$httpProvider.interceptors.push('authInterceptor');
$httpProvider.interceptors.push('httpPriorityInterceptor');
The interceptor looks like this:
function factory($injector, $q) {
var requestStack = [], // request stack
$http = null; // http service to be lazy loaded
return {
request: request,
responseError: responseError
};
//////////////////////////////////////////////////
function request(config) {
// Lazy load $http service
if (!$http) {
$http = $injector.get('$http');
}
if (!config.hasBeenRequested) {
config.hasBeenRequested = true;
config.priority = config.priority || 3;
console.log(config);
// add a copy of the configuration
// to prevent it from copying the timeout property
requestStack.push(angular.copy(config));
// sort each configuration by priority
requestStack = requestStack.sort(sort);
// cancel request by adding a resolved promise
config.timeout = $q.when();
}
// return config
return config;
}
function responseError(rejection) {
// check if there are requests to be processed
if (requestStack.length > 0) {
requestStack.reduceRight(function(promise, config) {
return promise.finally(function() {
return $http(config);
});
}, $q.when());
requestStack.length = 0;
}
// return rejected request
return $q.reject(rejection);
}
//////////////////////////////////////////////////
function sort(config1, config2) {
return config1.priority < config2.priority;
}
}
The problem is, it seems to be intercepting template requests too. I have no issue with that, but they are not resolving. Instead I get a lot of errors:
Error: [$templateRequest:tpload] Failed to load template: app/accounts/accounts.html (HTTP status: -1 )
Has anyone encountered this before? Is there something I can do to fix this?
you should know that every request such as html files , css file and ... comes into interceptor.
in your case you dont need to prioritize this files. so you can filter your request like:
if (config.url.toString().toLowerCase().includes("api")) {
//place your functionality
}

Any way to add local interceptor (only for one request)

Is there a way to add interceptor only specific to one particular request, like transformRespponse?
Doing like so will add global interceptor that will be executed on every request.
$httpProvider.interceptors.push(function($q) {
return {
'response': function(response) {
if (isBad(response)) {
return $q.reject(rejectionReason(response));
}
}
};
});
I think you over engineer the solution. $http already return promise with two callbacks
$http.get('your_url').then(onSuccess, onError)
function onSuccess (response) {
if (isBad(response)) {
return $q.reject(rejectionReason(response));
}
// do some magic
}
function onError(response){
return $q.reject(rejectionReason(response))
}
and really the isBad should probably happen on server side and return an http error so it's handled by onError
you can get request url inside response function
$httpProvider.interceptors.push(function($q) {
return {
'response': function(response) {
if(response.config.url == '/particular_request_url'){
//do something
}
return response
}
};
});

angular request busy interceptor

UPDATE
I think I solved it myself. Check my interceptor that I posted as a solution below.
ORIGINAL
I was wondering if it would be possible to write an http interceptor that can let the caller know how the request is doing.
Right now, when I want to call my backend, I wrap an $http call in a wrapper that sets attributes on an object I pass it:
publ.wrap = function(f, ctrl){
ctrl.busy = true;
ctrl.error = false;
return f()
.then(function(res){
ctrl.busy = false;
ctrl.result = res;
return res;
}).catch(function(err){
ctrl.busy = false;
ctrl.error = err;
ctrl.result = undefined;
})
};
publ.login = function(args, ctrl){
publ.wrap(function(){
return $http.post('http://localhost:3001/authenticate', {
username : args.username,
password : args.password
}).then(function(jwt){
$cookies.put('token', jwt);
})
}, ctrl);
};
In this case, I call login(authArgs, $scope.loginCtrl) in my login page controller. Then I use loginCtrl.busy, loginCtrl.result & loginCtrl.error in my login template.
I pretty much want every call I make to the backend to set these attributes and make them available to the views that initiate the request.
Using a wrapper function like this gets the job done, but I'm wondering if it can be done using an interceptor? It feels to me like that would provide a much cleaner request flow that doesn't require me to explicitly wrap all of my backend calls in my services.
Now I read up on httpInterceptors, and can't seem to find a way to have them set attributes on a user-provided object. The closes thing I found was this article that has an example ( Timestamp Marker (request and response interceptors) ) where they add attributes to the config object in both the request and response interceptor stages.They don't show how to access the config object inside the responseError stage or in the caller controller.
Any help would be greatly appreciated :)
I use Angular events to handle stuff like this- for example:
.controller('parentCtrl', function($scope,$rootScope) {
$rootScope.$on('loading',function(e,_statusObj.loading) {
$scope.loading = _statusObj.loading;
if(!!_statusObj.msg) {
alert(_statusObj.msg);
}
});
})
.controller('childCtrl', function($scope,$http) {
$scope.myAjaxCall = function(_url,_data) {
$scope.$emit('loading',{ loading: true});
$http.post(_url,_data).success(function(_response) {
$scope.$emit('loading',{ loading: false });
})
.error(function(_error) {
$scope.$emit('loading',{
loading : false,
msg : _error.message
});
});
}
});
I managed to get the interceptor working. Apparently we CAN access the config file in all interceptor phases:
/******************************************
SETUP BUSY/ERROR/DATA HTTP INTERCEPTOR
*******************************************/
.config(function($httpProvider){
$httpProvider.interceptors.push(function($q) {
return {
request : function(config) {
if(config.ctrl){
config.ctrl.busy = true;
config.ctrl.error = false;
config.ctrl.data = undefined;
}
return config;
},
response : function(response) {
if(response.config && response.config.ctrl){
response.config.ctrl.busy = false;
response.config.ctrl.data = response.data;
}
return response;
},
responseError : function(response){
// note: maybe use a different error message for different kinds of responses?
var error = response.status + " "+response.statusText+" - "+response.data;
if(response.config && response.config.ctrl){
response.config.ctrl.busy = false;
response.config.ctrl.error = error;
}
return $q.reject(error);
}
};
});
})

console service response inside controller

i have written a service with take parameter on the basis of which it response me with the response of http request.
this.getPaymentDueDetails=function(date){
this.getfromRemote('paymentdue/'+btoa(date))
.success(function(response){
return response;
})
.error(function(response){
return false;
})
}
getfromRemote is my another service which make http request
now i am trying to get the response of this service call inside my controller function
$scope.callDueReports=function(blockNum){
var data;
data=myAngService.getPaymentDueDetails('2015-04-20');
console.log(data);
}
its quite obvious that i wont get any thing in data as the page loads initially but i want the result of getPaymentDueDetails int it.
Please modify your service to return the promise like below.
this.getPaymentDueDetails = function(date) {
return this.getfromRemote('paymentdue/' + btoa(date));
};
And in controller check if the promise is resolved.
$scope.callDueReports = function(blockNum) {
var data;
myAngService.getPaymentDueDetails('2015-04-20').then(function(dataFromService) {
data = dataFromService;
console.log(data);
})
.catch(function(response) {
console.error('error');
});
};

Cancelling a request with a $http interceptor?

I'm trying to figure out if it is possible to use a $http interceptor to cancel a request before it even happens.
There is a button that triggers a request but if the user double-clicks it I do not want the same request to get triggered twice.
Now, I realize that there's several ways to solve this, and we do already have a working solution where we wrap $http in a service that keeps track of requests that are currently pending and simply ignores new requests with the same method, url and data.
Basically this is the behaviour I am trying to do with an interceptor:
factory('httpService', ['$http', function($http) {
var pendingCalls = {};
var createKey = function(url, data, method) {
return method + url + JSON.stringify(data);
};
var send = function(url, data, method) {
var key = createKey(url, data, method);
if (pendingCalls[key]) {
return pendingCalls[key];
}
var promise = $http({
method: method,
url: url,
data: data
});
pendingCalls[key] = promise;
promise.finally(function() {
delete pendingCalls[key];
});
return promise;
};
return {
post: function(url, data) {
return send(url, data, 'POST');
}
}
}])
When I look at the API for $http interceptors it does not seem to be a way to achieve this. I have access to the config object but that's about it.
Am I attempting to step outside the boundaries of what interceptors can be used for here or is there a way to do it?
according to $http documentation, you can return your own config from request interceptor.
try something like this:
config(function($httpProvider) {
var cache = {};
$httpProvider.interceptors.push(function() {
return {
response : function(config) {
var key = createKey(config);
var cached = cache[key];
return cached ? cached : cached[key];
}
}
});
}
Very old question, but I'll give a shot to handle this situation.
If I understood correctly, you are trying to:
1 - Start a request and register something to refer back to it;
2 - If another request takes place, to the same endpoint, you want to retrieve that first reference and drop the request in it.
This might be handled by a request timeout in the $http config object. On the interceptor, you can verify it there's one registered on the current request, if not, you can setup one, keep a reference to it and handle if afterwards:
function DropoutInterceptor($injector) {
var $q = $q || $injector.get('$q');
var dropouts = {};
return {
'request': function(config) {
// I'm using the request's URL here to make
// this reference, but this can be bad for
// some situations.
if (dropouts.hasOwnProperty(config.url)) {
// Drop the request
dropouts[config.url].resolve();
}
dropouts[config.url] = $q.defer();
// If the request already have one timeout
// defined, keep it, othwerwise, set up ours.
config.timeout = config.timeout || dropouts[config.url];
return config;
},
'requestError': function(reason) {
delete dropouts[reason.config.url];
return $q.reject(reason);
},
'response': function(response) {
delete dropouts[response.config.url];
return response;
},
'responseError': function(reason) {
delete dropouts[reason.config.url];
return $q.reject(reason);
}
};
}

Resources