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

Commit 93fe2cf

Browse files
committed
feat(errorHandlingConfig): make the length of URL refrence in error messages configurable
Closes #14744 Closes #15707
1 parent 71f437c commit 93fe2cf

File tree

6 files changed

+120
-17
lines changed

6 files changed

+120
-17
lines changed

src/.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"toString": false,
1818
"minErrConfig": false,
1919
"errorHandlingConfig": false,
20-
"isValidObjectMaxDepth": false,
20+
"isValidNumberForMinErrConfig": false,
2121
"ngMinErr": false,
2222
"_angular": false,
2323
"angularModule": false,

src/Angular.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
toString,
1313
minErrConfig,
1414
errorHandlingConfig,
15-
isValidObjectMaxDepth,
15+
isValidNumberForMinErrConfig,
1616
ngMinErr,
1717
angularModule,
1818
uid,
@@ -129,7 +129,8 @@ var VALIDITY_STATE_PROPERTY = 'validity';
129129
var hasOwnProperty = Object.prototype.hasOwnProperty;
130130

131131
var minErrConfig = {
132-
objectMaxDepth: 5
132+
objectMaxDepth: 5,
133+
urlMaxLength: 2000
133134
};
134135

135136
/**
@@ -143,6 +144,7 @@ var minErrConfig = {
143144
* current configuration if used as a getter. The following options are supported:
144145
*
145146
* - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
147+
* - **urlMaxLength**: The maximum length of the URL reference in the error messages.
146148
*
147149
* Omitted or undefined options will leave the corresponding configuration values unchanged.
148150
*
@@ -152,11 +154,18 @@ var minErrConfig = {
152154
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
153155
* non-positive or non-numeric value, removes the max depth limit.
154156
* Default: 5
157+
*
158+
* * `urlMaxLength` **{Number}** - The max length of the URL reference in the error messages. Setting to a
159+
* non-positive or non-numeric value, removes the url max length limit.
160+
* Default: 2000
155161
*/
156162
function errorHandlingConfig(config) {
157163
if (isObject(config)) {
158164
if (isDefined(config.objectMaxDepth)) {
159-
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
165+
minErrConfig.objectMaxDepth = isValidNumberForMinErrConfig(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
166+
}
167+
if (isDefined(config.urlMaxLength)) {
168+
minErrConfig.urlMaxLength = isValidNumberForMinErrConfig(config.urlMaxLength) ? config.urlMaxLength : NaN;
160169
}
161170
} else {
162171
return minErrConfig;
@@ -168,8 +177,8 @@ function errorHandlingConfig(config) {
168177
* @param {Number} maxDepth
169178
* @return {boolean}
170179
*/
171-
function isValidObjectMaxDepth(maxDepth) {
172-
return isNumber(maxDepth) && maxDepth > 0;
180+
function isValidNumberForMinErrConfig(num) {
181+
return isNumber(num) && num > 0;
173182
}
174183

175184
/**
@@ -897,7 +906,7 @@ function arrayRemove(array, value) {
897906
function copy(source, destination, maxDepth) {
898907
var stackSource = [];
899908
var stackDest = [];
900-
maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
909+
maxDepth = isValidNumberForMinErrConfig(maxDepth) ? maxDepth : NaN;
901910

902911
if (destination) {
903912
if (isTypedArray(destination) || isArrayBuffer(destination)) {

src/minErr.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,19 @@ function minErr(module, ErrorConstructor) {
5151
return match;
5252
});
5353

54-
message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
54+
var url = 'http://errors.angularjs.org/"NG_VERSION_FULL"/' +
5555
(module ? module + '/' : '') + code;
5656

5757
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
58-
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
58+
url += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
59+
60+
if (url.length > minErrConfig.urlMaxLength) {
61+
url = url.substr(0, minErrConfig.urlMaxLength - 3) + '...';
62+
break;
63+
}
5964
}
6065

66+
message += '\n' + url;
6167
return new ErrorConstructor(message);
6268
};
6369
}

src/stringify.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function serializeObject(obj, maxDepth) {
88
// There is no direct way to stringify object until reaching a specific depth
99
// and a very deep object can cause a performance issue, so we copy the object
1010
// based on this specific depth and then stringify it.
11-
if (isValidObjectMaxDepth(maxDepth)) {
11+
if (isValidNumberForMinErrConfig(maxDepth)) {
1212
obj = copy(obj, null, maxDepth);
1313
}
1414
return JSON.stringify(obj, function(key, val) {

test/AngularSpec.js

+54
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,67 @@ Float32Array, Float64Array, */
77

88
describe('angular', function() {
99
var element, document;
10+
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
11+
var originalUrlMaxLengthInErrorMessage = minErrConfig.urlMaxLength;
1012

1113
beforeEach(function() {
1214
document = window.document;
1315
});
1416

1517
afterEach(function() {
1618
dealoc(element);
19+
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
20+
minErrConfig.urlMaxLength = originalUrlMaxLengthInErrorMessage;
21+
});
22+
23+
describe('errorHandlingConfig', function() {
24+
it('should get default objectMaxDepth', function() {
25+
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
26+
});
27+
28+
it('should set objectMaxDepth only', function() {
29+
errorHandlingConfig({objectMaxDepth: 3});
30+
expect(errorHandlingConfig()).toEqual({
31+
objectMaxDepth: 3,
32+
urlMaxLength: originalUrlMaxLengthInErrorMessage
33+
});
34+
});
35+
36+
it('should not change objectMaxDepth when undefined is supplied', function() {
37+
errorHandlingConfig({objectMaxDepth: undefined});
38+
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
39+
});
40+
41+
they('should set objectMaxDepth to NAN when $prop is supplied',
42+
[NaN, null, true, false, -1, 0], function(maxDepth) {
43+
errorHandlingConfig({objectMaxDepth: maxDepth});
44+
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
45+
}
46+
);
47+
48+
it('should get default urlMaxLength', function() {
49+
expect(errorHandlingConfig().urlMaxLength).toBe(2000);
50+
});
51+
52+
it('should set urlMaxLength only', function() {
53+
errorHandlingConfig({urlMaxLength: 500});
54+
expect(errorHandlingConfig()).toEqual({
55+
'objectMaxDepth': originalObjectMaxDepthInErrorMessage,
56+
'urlMaxLength': 500
57+
});
58+
});
59+
60+
it('should not change urlMaxLength when undefined is supplied', function() {
61+
errorHandlingConfig({urlMaxLength: undefined});
62+
expect(errorHandlingConfig().urlMaxLength).toBe(originalUrlMaxLengthInErrorMessage);
63+
});
64+
65+
they('should set urlMaxLength to NAN when $prop is supplied',
66+
[NaN, null, true, false, -1, 0], function(maxLength) {
67+
errorHandlingConfig({urlMaxLength: maxLength});
68+
expect(errorHandlingConfig().urlMaxLength).toBeNaN();
69+
}
70+
);
1771
});
1872

1973
describe('case', function() {

test/minErrSpec.js

+41-7
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,21 @@ describe('minErr', function() {
1010
testError = minErr('test');
1111

1212
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
13+
var originalUrlMaxLengthInErrorMessage = minErrConfig.urlMaxLength;
1314
afterEach(function() {
1415
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
16+
minErrConfig.urlMaxLength = originalUrlMaxLengthInErrorMessage;
1517
});
1618

19+
function extractUrlFromErrorMessage(message) {
20+
var match = message.match(/http[\s\S]*\?p0=/);
21+
var urlStartAt = message.indexOf(match[0]);
22+
if (urlStartAt < 0) {
23+
throw new Error('Could not find url');
24+
}
25+
return message.slice(urlStartAt);
26+
}
27+
1728
it('should return an Error factory', function() {
1829
var myError = testError('test', 'Oops');
1930
expect(myError instanceof Error).toBe(true);
@@ -78,32 +89,28 @@ describe('minErr', function() {
7889

7990
var myError = testError('26', 'a when objectMaxDepth is default=5 is {0}', a);
8091
expect(myError.message).toMatch(/a when objectMaxDepth is default=5 is {"b":{"c":{"d":{"e":{"f":"..."}}}}}/);
81-
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
92+
8293

8394
errorHandlingConfig({objectMaxDepth: 1});
8495
myError = testError('26', 'a when objectMaxDepth is set to 1 is {0}', a);
8596
expect(myError.message).toMatch(/a when objectMaxDepth is set to 1 is {"b":"..."}/);
86-
expect(errorHandlingConfig().objectMaxDepth).toBe(1);
8797

8898
errorHandlingConfig({objectMaxDepth: 2});
8999
myError = testError('26', 'a when objectMaxDepth is set to 2 is {0}', a);
90100
expect(myError.message).toMatch(/a when objectMaxDepth is set to 2 is {"b":{"c":"..."}}/);
91-
expect(errorHandlingConfig().objectMaxDepth).toBe(2);
92101

93102
errorHandlingConfig({objectMaxDepth: undefined});
94103
myError = testError('26', 'a when objectMaxDepth is set to undefined is {0}', a);
95104
expect(myError.message).toMatch(/a when objectMaxDepth is set to undefined is {"b":{"c":"..."}}/);
96-
expect(errorHandlingConfig().objectMaxDepth).toBe(2);
97105
});
98106

99107
they('should handle arguments that are objects and ignore max depth when objectMaxDepth = $prop',
100108
[NaN, null, true, false, -1, 0], function(maxDepth) {
101-
var a = {b: {c: {d: 1}}};
109+
var a = {b: {c: {d: {e: {f: {g: 1}}}}}};
102110

103111
errorHandlingConfig({objectMaxDepth: maxDepth});
104112
var myError = testError('26', 'a is {0}', a);
105-
expect(myError.message).toMatch(/a is {"b":{"c":{"d":1}}}/);
106-
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
113+
expect(myError.message).toMatch(/a is {"b":{"c":{"d":{"e":{"f":{"g":1}}}}}}/);
107114
}
108115
);
109116

@@ -143,4 +150,31 @@ describe('minErr', function() {
143150
expect(testError('acode', 'aproblem', 'a', 'b', 'value with space').message)
144151
.toMatch(/^[\s\S]*\?p0=a&p1=b&p2=value%20with%20space$/);
145152
});
153+
154+
it('should slice error reference URL in the message if it exceeds url max length', function() {
155+
var a = new Array(3000).join('a');
156+
var myError = testError('26', 'a is {0}', a);
157+
var url = extractUrlFromErrorMessage(myError.message);
158+
expect(url.length).toBe(2000);
159+
160+
errorHandlingConfig({urlMaxLength: 500});
161+
myError = testError('26', 'a is {0}', a);
162+
url = extractUrlFromErrorMessage(myError.message);
163+
expect(url.length).toBe(500);
164+
165+
errorHandlingConfig({urlMaxLength: undefined});
166+
myError = testError('26', 'a is {0}', a);
167+
url = extractUrlFromErrorMessage(myError.message);
168+
expect(url.length).toBe(500);
169+
});
170+
171+
they('should ignore url max length when urlMaxLength = $prop',
172+
[NaN, null, true, false, -1, 0], function(maxLength) {
173+
var a = new Array(3000).join('a');
174+
errorHandlingConfig({urlMaxLength: maxLength});
175+
var myError = testError('26', 'a is {0}', a);
176+
var url = extractUrlFromErrorMessage(myError.message);
177+
expect(url.length).toBeGreaterThan(3000);
178+
}
179+
);
146180
});

0 commit comments

Comments
 (0)