Skip to content

Commit ec97c68

Browse files
authored
Merge pull request axios#1395 from codeclown/instance-options
Fixing axios#385 - Keep defaults local to instance
2 parents dd16944 + 4e8039e commit ec97c68

File tree

9 files changed

+252
-10
lines changed

9 files changed

+252
-10
lines changed

lib/axios.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
var utils = require('./utils');
44
var bind = require('./helpers/bind');
55
var Axios = require('./core/Axios');
6+
var mergeConfig = require('./core/mergeConfig');
67
var defaults = require('./defaults');
78

89
/**
@@ -32,7 +33,7 @@ axios.Axios = Axios;
3233

3334
// Factory for creating new instances
3435
axios.create = function create(instanceConfig) {
35-
return createInstance(utils.merge(defaults, instanceConfig));
36+
return createInstance(mergeConfig(axios.defaults, instanceConfig));
3637
};
3738

3839
// Expose Cancel & CancelToken

lib/core/Axios.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

3-
var defaults = require('./../defaults');
43
var utils = require('./../utils');
54
var InterceptorManager = require('./InterceptorManager');
65
var dispatchRequest = require('./dispatchRequest');
6+
var mergeConfig = require('./mergeConfig');
77

88
/**
99
* Create a new instance of Axios
@@ -27,13 +27,14 @@ Axios.prototype.request = function request(config) {
2727
/*eslint no-param-reassign:0*/
2828
// Allow for axios('example/url'[, config]) a la fetch API
2929
if (typeof config === 'string') {
30-
config = utils.merge({
31-
url: arguments[0]
32-
}, arguments[1]);
30+
config = arguments[1] || {};
31+
config.url = arguments[0];
32+
} else {
33+
config = config || {};
3334
}
3435

35-
config = utils.merge(defaults, this.defaults, config);
36-
config.method = config.method.toLowerCase();
36+
config = mergeConfig(this.defaults, config);
37+
config.method = config.method ? config.method.toLowerCase() : 'get';
3738

3839
// Hook up interceptors middleware
3940
var chain = [dispatchRequest, undefined];

lib/core/mergeConfig.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
var utils = require('../utils');
4+
5+
/**
6+
* Config-specific merge-function which creates a new config-object
7+
* by merging two configuration objects together.
8+
*
9+
* @param {Object} config1
10+
* @param {Object} config2
11+
* @returns {Object} New object resulting from merging config2 to config1
12+
*/
13+
module.exports = function mergeConfig(config1, config2) {
14+
// eslint-disable-next-line no-param-reassign
15+
config2 = config2 || {};
16+
var config = {};
17+
18+
utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) {
19+
if (typeof config2[prop] !== 'undefined') {
20+
config[prop] = config2[prop];
21+
}
22+
});
23+
24+
utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) {
25+
if (utils.isObject(config2[prop])) {
26+
config[prop] = utils.deepMerge(config1[prop], config2[prop]);
27+
} else if (typeof config2[prop] !== 'undefined') {
28+
config[prop] = config2[prop];
29+
} else if (utils.isObject(config1[prop])) {
30+
config[prop] = utils.deepMerge(config1[prop]);
31+
} else if (typeof config1[prop] !== 'undefined') {
32+
config[prop] = config1[prop];
33+
}
34+
});
35+
36+
utils.forEach([
37+
'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
38+
'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
39+
'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength',
40+
'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken',
41+
'socketPath'
42+
], function defaultToConfig2(prop) {
43+
if (typeof config2[prop] !== 'undefined') {
44+
config[prop] = config2[prop];
45+
} else if (typeof config1[prop] !== 'undefined') {
46+
config[prop] = config1[prop];
47+
}
48+
});
49+
50+
return config;
51+
};

lib/defaults.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ function getDefaultAdapter() {
2626
}
2727

2828
var defaults = {
29-
method: 'get',
3029
adapter: getDefaultAdapter(),
3130

3231
transformRequest: [function transformRequest(data, headers) {

lib/utils.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,32 @@ function merge(/* obj1, obj2, obj3, ... */) {
260260
return result;
261261
}
262262

263+
/**
264+
* Function equal to merge with the difference being that no reference
265+
* to original objects is kept.
266+
*
267+
* @see merge
268+
* @param {Object} obj1 Object to merge
269+
* @returns {Object} Result of all merge properties
270+
*/
271+
function deepMerge(/* obj1, obj2, obj3, ... */) {
272+
var result = {};
273+
function assignValue(val, key) {
274+
if (typeof result[key] === 'object' && typeof val === 'object') {
275+
result[key] = deepMerge(result[key], val);
276+
} else if (typeof val === 'object') {
277+
result[key] = deepMerge({}, val);
278+
} else {
279+
result[key] = val;
280+
}
281+
}
282+
283+
for (var i = 0, l = arguments.length; i < l; i++) {
284+
forEach(arguments[i], assignValue);
285+
}
286+
return result;
287+
}
288+
263289
/**
264290
* Extends object a by mutably adding to it the properties of object b.
265291
*
@@ -298,6 +324,7 @@ module.exports = {
298324
isStandardBrowserEnv: isStandardBrowserEnv,
299325
forEach: forEach,
300326
merge: merge,
327+
deepMerge: deepMerge,
301328
extend: extend,
302329
trim: trim
303330
};

test/specs/core/mergeConfig.spec.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
var defaults = require('../../../lib/defaults');
2+
var mergeConfig = require('../../../lib/core/mergeConfig');
3+
4+
describe('core::mergeConfig', function() {
5+
it('should accept undefined for second argument', function() {
6+
expect(mergeConfig(defaults, undefined)).toEqual(defaults);
7+
});
8+
9+
it('should accept an object for second argument', function() {
10+
expect(mergeConfig(defaults, {})).toEqual(defaults);
11+
});
12+
13+
it('should not leave references', function() {
14+
var merged = mergeConfig(defaults, {});
15+
expect(merged).not.toBe(defaults);
16+
expect(merged.headers).not.toBe(defaults.headers);
17+
});
18+
19+
it('should allow setting request options', function() {
20+
var config = {
21+
url: '__sample url__',
22+
method: '__sample method__',
23+
params: '__sample params__',
24+
data: { foo: true }
25+
};
26+
var merged = mergeConfig(defaults, config);
27+
expect(merged.url).toEqual(config.url);
28+
expect(merged.method).toEqual(config.method);
29+
expect(merged.params).toEqual(config.params);
30+
expect(merged.data).toEqual(config.data);
31+
});
32+
33+
it('should not inherit request options', function() {
34+
var localDefaults = {
35+
url: '__sample url__',
36+
method: '__sample method__',
37+
params: '__sample params__',
38+
data: { foo: true }
39+
};
40+
var merged = mergeConfig(localDefaults, {});
41+
expect(merged.url).toEqual(undefined);
42+
expect(merged.method).toEqual(undefined);
43+
expect(merged.params).toEqual(undefined);
44+
expect(merged.data).toEqual(undefined);
45+
});
46+
47+
it('should merge auth, headers, proxy with defaults', function() {
48+
expect(mergeConfig({ auth: undefined }, { auth: { user: 'foo', pass: 'test' } })).toEqual({
49+
auth: { user: 'foo', pass: 'test' }
50+
});
51+
expect(mergeConfig({ auth: { user: 'foo', pass: 'test' } }, { auth: { pass: 'foobar' } })).toEqual({
52+
auth: { user: 'foo', pass: 'foobar' }
53+
});
54+
});
55+
56+
it('should overwrite auth, headers, proxy with a non-object value', function() {
57+
expect(mergeConfig({ auth: { user: 'foo', pass: 'test' } }, { auth: false })).toEqual({
58+
auth: false
59+
});
60+
expect(mergeConfig({ auth: { user: 'foo', pass: 'test' } }, { auth: null })).toEqual({
61+
auth: null
62+
});
63+
});
64+
65+
it('should allow setting other options', function() {
66+
var merged = mergeConfig(defaults, { timeout: 123 });
67+
expect(merged.timeout).toEqual(123);
68+
});
69+
});

test/specs/defaults.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,14 @@ describe('defaults', function () {
148148
});
149149
});
150150

151-
it('should be used by custom instance if set after instance created', function (done) {
151+
it('should not be used by custom instance if set after instance created', function (done) {
152152
var instance = axios.create();
153153
axios.defaults.baseURL = 'http://example.org/';
154154

155155
instance.get('/foo');
156156

157157
getAjaxRequest().then(function (request) {
158-
expect(request.url).toBe('http://example.org/foo');
158+
expect(request.url).toBe('/foo');
159159
done();
160160
});
161161
});

test/specs/options.spec.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,32 @@ describe('options', function () {
8181
done();
8282
});
8383
});
84+
85+
it('should change only the baseURL of the specified instance', function() {
86+
var instance1 = axios.create();
87+
var instance2 = axios.create();
88+
89+
instance1.defaults.baseURL = 'http://instance1.example.com/';
90+
91+
expect(instance2.defaults.baseURL).not.toBe('http://instance1.example.com/');
92+
});
93+
94+
it('should change only the headers of the specified instance', function() {
95+
var instance1 = axios.create();
96+
var instance2 = axios.create();
97+
98+
instance1.defaults.headers.common.Authorization = 'faketoken';
99+
instance2.defaults.headers.common.Authorization = 'differentfaketoken';
100+
101+
instance1.defaults.headers.common['Content-Type'] = 'application/xml';
102+
instance2.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded';
103+
104+
expect(axios.defaults.headers.common.Authorization).toBe(undefined);
105+
expect(instance1.defaults.headers.common.Authorization).toBe('faketoken');
106+
expect(instance2.defaults.headers.common.Authorization).toBe('differentfaketoken');
107+
108+
expect(axios.defaults.headers.common['Content-Type']).toBe(undefined);
109+
expect(instance1.defaults.headers.common['Content-Type']).toBe('application/xml');
110+
expect(instance2.defaults.headers.common['Content-Type']).toBe('application/x-www-form-urlencoded');
111+
});
84112
});

test/specs/utils/deepMerge.spec.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
var deepMerge = require('../../../lib/utils').deepMerge;
2+
3+
describe('utils::deepMerge', function () {
4+
it('should be immutable', function () {
5+
var a = {};
6+
var b = {foo: 123};
7+
var c = {bar: 456};
8+
9+
deepMerge(a, b, c);
10+
11+
expect(typeof a.foo).toEqual('undefined');
12+
expect(typeof a.bar).toEqual('undefined');
13+
expect(typeof b.bar).toEqual('undefined');
14+
expect(typeof c.foo).toEqual('undefined');
15+
});
16+
17+
it('should deepMerge properties', function () {
18+
var a = {foo: 123};
19+
var b = {bar: 456};
20+
var c = {foo: 789};
21+
var d = deepMerge(a, b, c);
22+
23+
expect(d.foo).toEqual(789);
24+
expect(d.bar).toEqual(456);
25+
});
26+
27+
it('should deepMerge recursively', function () {
28+
var a = {foo: {bar: 123}};
29+
var b = {foo: {baz: 456}, bar: {qux: 789}};
30+
31+
expect(deepMerge(a, b)).toEqual({
32+
foo: {
33+
bar: 123,
34+
baz: 456
35+
},
36+
bar: {
37+
qux: 789
38+
}
39+
});
40+
});
41+
42+
it('should remove all references from nested objects', function () {
43+
var a = {foo: {bar: 123}};
44+
var b = {};
45+
var d = deepMerge(a, b);
46+
47+
expect(d).toEqual({
48+
foo: {
49+
bar: 123
50+
}
51+
});
52+
53+
expect(d.foo).not.toBe(a.foo);
54+
});
55+
56+
it('handles null and undefined arguments', function () {
57+
expect(deepMerge(undefined, undefined)).toEqual({});
58+
expect(deepMerge(undefined, {foo: 123})).toEqual({foo: 123});
59+
expect(deepMerge({foo: 123}, undefined)).toEqual({foo: 123});
60+
61+
expect(deepMerge(null, null)).toEqual({});
62+
expect(deepMerge(null, {foo: 123})).toEqual({foo: 123});
63+
expect(deepMerge({foo: 123}, null)).toEqual({foo: 123});
64+
});
65+
});
66+

0 commit comments

Comments
 (0)