Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 83b5ddb

Browse files
feat($jsonpCallbacks): new service to abstract how JSONP callbacks are handled
You can now override this service if you have specific requirements about the behaviour and formatting of the JSON_CALLBACK that is sent to the server for `$http.jsonp` requests. Closes #14795
1 parent d406a15 commit 83b5ddb

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed

angularFiles.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var angularFiles = {
2828
'src/ng/httpBackend.js',
2929
'src/ng/interpolate.js',
3030
'src/ng/interval.js',
31+
'src/ng/jsonpCallbacks.js',
3132
'src/ng/locale.js',
3233
'src/ng/location.js',
3334
'src/ng/log.js',

src/AngularPublic.js

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
$HttpParamSerializerJQLikeProvider,
7878
$HttpBackendProvider,
7979
$xhrFactoryProvider,
80+
$jsonpCallbacksProvider,
8081
$LocationProvider,
8182
$LogProvider,
8283
$ParseProvider,
@@ -239,6 +240,7 @@ function publishExternalAPI(angular) {
239240
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
240241
$httpBackend: $HttpBackendProvider,
241242
$xhrFactory: $xhrFactoryProvider,
243+
$jsonpCallbacks: $jsonpCallbacksProvider,
242244
$location: $LocationProvider,
243245
$log: $LogProvider,
244246
$parse: $ParseProvider,

src/ng/jsonpCallbacks.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
3+
/**
4+
* @ngdoc service
5+
* @name $jsonpCallbacks
6+
* @requires $window
7+
* @description
8+
* This service handles the lifecycle of callbacks to handle JSONP requests.
9+
* Override this service if you wish to customise where the callbacks are stored and
10+
* how they vary compared to the requested url.
11+
*/
12+
var $jsonpCallbacksProvider = function() {
13+
this.$get = ['$window', function($window) {
14+
var counter = 0;
15+
$window.angular.callbacks = {};
16+
var callbackMap = {};
17+
18+
function createCallback(callbackId) {
19+
var callback = function(data) {
20+
callback.data = data;
21+
callback.called = true;
22+
};
23+
callback.id = callbackId;
24+
return callback;
25+
}
26+
27+
return {
28+
/**
29+
* @ngdoc method
30+
* @name $jsonpCallbacks#createCallback
31+
* @param {string} url the url of the JSONP request
32+
* @returns {string} the callback path to send to the server as part of the JSONP request
33+
* @description
34+
* {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
35+
* to pass to the server, which will be used to call the callback with its payload in the JSONP response.
36+
*/
37+
createCallback: function(url) {
38+
var callbackId = '_' + (counter++).toString(36);
39+
var callbackPath = 'angular.callbacks.' + callbackId;
40+
var callback = createCallback(callbackId);
41+
callbackMap[callbackPath] = $window.angular.callbacks[callbackId] = callback;
42+
return callbackPath;
43+
},
44+
/**
45+
* @ngdoc method
46+
* @name $jsonpCallbacks#wasCalled
47+
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
48+
* @returns {boolean} whether the callback has been called, as a result of the JSONP response
49+
* @description
50+
* {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
51+
* callback that was passed in the request.
52+
*/
53+
wasCalled: function(callbackPath) {
54+
return callbackMap[callbackPath].called;
55+
},
56+
/**
57+
* @ngdoc method
58+
* @name $jsonpCallbacks#getResponse
59+
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
60+
* @returns {*} the data received from the response via the registered callback
61+
* @description
62+
* {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
63+
* in the JSONP response.
64+
*/
65+
getResponse: function(callbackPath) {
66+
return callbackMap[callbackPath].data;
67+
},
68+
/**
69+
* @ngdoc method
70+
* @name $jsonpCallbacks#removeCallback
71+
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
72+
* @description
73+
* {@link $httpBackend} calls this method to remove the callback after the JSONP request has
74+
* completed or timed-out.
75+
*/
76+
removeCallback: function(callbackPath) {
77+
var callback = callbackMap[callbackPath];
78+
delete $window.angular.callbacks[callback.id];
79+
delete callbackMap[callbackPath];
80+
}
81+
};
82+
}];
83+
};

test/ng/jsonpCallbacksSpec.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
3+
describe('$jsonpCallbacks', function() {
4+
5+
beforeEach(module(function($provide) {
6+
// mock out the $window object
7+
$provide.value('$window', { angular: {} });
8+
}));
9+
10+
describe('createCallback(url)', function() {
11+
12+
it('should return a new unique path to a callback function on each call', inject(function($jsonpCallbacks) {
13+
var path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
14+
expect(path).toEqual('angular.callbacks._0');
15+
16+
path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
17+
expect(path).toEqual('angular.callbacks._1');
18+
19+
path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
20+
expect(path).toEqual('angular.callbacks._2');
21+
22+
path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
23+
expect(path).toEqual('angular.callbacks._3');
24+
}));
25+
26+
it('should add a callback method to the $window.angular.callbacks collection on each call', inject(function($window, $jsonpCallbacks) {
27+
$jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
28+
expect($window.angular.callbacks._0).toEqual(jasmine.any(Function));
29+
30+
$jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
31+
expect($window.angular.callbacks._1).toEqual(jasmine.any(Function));
32+
33+
$jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
34+
expect($window.angular.callbacks._2).toEqual(jasmine.any(Function));
35+
36+
$jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
37+
expect($window.angular.callbacks._3).toEqual(jasmine.any(Function));
38+
}));
39+
});
40+
41+
42+
describe('wasCalled(callbackPath)', function() {
43+
44+
it('should return true once the callback has been called', inject(function($window, $jsonpCallbacks) {
45+
var path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
46+
expect($jsonpCallbacks.wasCalled(path)).toBeFalsy();
47+
var response = {};
48+
$window.angular.callbacks._0(response);
49+
expect($jsonpCallbacks.wasCalled(path)).toBeTruthy();
50+
}));
51+
});
52+
53+
54+
describe('getResponse(callbackPath)', function() {
55+
56+
it('should retrieve the data from when the callback was called', inject(function($window, $jsonpCallbacks) {
57+
var path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
58+
var response = {};
59+
$window.angular.callbacks._0(response);
60+
var result = $jsonpCallbacks.getResponse(path);
61+
expect(result).toBe(response);
62+
}));
63+
});
64+
65+
describe('removeCallback(calbackPath)', function() {
66+
67+
it('should remove the callback', inject(function($window, $jsonpCallbacks) {
68+
var path = $jsonpCallbacks.createCallback('http://some.dummy.com/jsonp/request');
69+
$jsonpCallbacks.removeCallback(path);
70+
expect($window.angular.callbacks._0).toBeUndefined();
71+
}));
72+
});
73+
});

0 commit comments

Comments
 (0)