rollbar 2.25.1 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/rollbar.js +261 -61
- package/dist/rollbar.js.map +1 -1
- package/dist/rollbar.min.js +1 -1
- package/dist/rollbar.min.js.map +1 -1
- package/dist/rollbar.named-amd.js +261 -61
- package/dist/rollbar.named-amd.js.map +1 -1
- package/dist/rollbar.named-amd.min.js +1 -1
- package/dist/rollbar.named-amd.min.js.map +1 -1
- package/dist/rollbar.noconflict.umd.js +261 -61
- package/dist/rollbar.noconflict.umd.js.map +1 -1
- package/dist/rollbar.noconflict.umd.min.js +1 -1
- package/dist/rollbar.noconflict.umd.min.js.map +1 -1
- package/dist/rollbar.snippet.js +1 -1
- package/dist/rollbar.umd.js +261 -61
- package/dist/rollbar.umd.js.map +1 -1
- package/dist/rollbar.umd.min.js +1 -1
- package/dist/rollbar.umd.min.js.map +1 -1
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/apiUtility.js +14 -2
- package/src/browser/telemetry.js +6 -1
- package/src/browser/transforms.js +12 -7
- package/src/browser/transport/fetch.js +35 -0
- package/src/browser/transport/xhr.js +159 -0
- package/src/browser/transport.js +29 -166
- package/src/defaults.js +1 -1
- package/src/utility/headers.js +94 -0
- package/test/apiUtility.test.js +54 -0
- package/test/browser.rollbar.test.js +101 -28
- package/test/browser.transforms.test.js +15 -1
- package/test/browser.transport.test.js +59 -0
package/test/apiUtility.test.js
CHANGED
|
@@ -105,6 +105,60 @@ describe('getTransportFromOptions', function() {
|
|
|
105
105
|
expect(t.proxy).to.eql(options.proxy);
|
|
106
106
|
expect(t.timeout).to.eql(undefined);
|
|
107
107
|
});
|
|
108
|
+
describe('getTransportFromOptions', function() {
|
|
109
|
+
var defaults = {
|
|
110
|
+
hostname: 'api.com',
|
|
111
|
+
protocol: 'https:',
|
|
112
|
+
path: '/api/1',
|
|
113
|
+
search: '?abc=456',
|
|
114
|
+
};
|
|
115
|
+
var url = {
|
|
116
|
+
parse: function(_) {
|
|
117
|
+
return {
|
|
118
|
+
hostname: 'whatever.com',
|
|
119
|
+
protocol: 'http:',
|
|
120
|
+
pathname: '/api/42'
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
it('should use xhr by default', function(done) {
|
|
125
|
+
var options = {};
|
|
126
|
+
var t = u.getTransportFromOptions(options, defaults, url);
|
|
127
|
+
expect(t.transport).to.eql('xhr');
|
|
128
|
+
done();
|
|
129
|
+
});
|
|
130
|
+
it('should use fetch when requested', function(done) {
|
|
131
|
+
var options = {defaultTransport: 'fetch'};
|
|
132
|
+
var t = u.getTransportFromOptions(options, defaults, url);
|
|
133
|
+
expect(t.transport).to.eql('fetch');
|
|
134
|
+
done();
|
|
135
|
+
});
|
|
136
|
+
it('should use xhr when requested', function(done) {
|
|
137
|
+
var options = {defaultTransport: 'xhr'};
|
|
138
|
+
var t = u.getTransportFromOptions(options, defaults, url);
|
|
139
|
+
expect(t.transport).to.eql('xhr');
|
|
140
|
+
done();
|
|
141
|
+
});
|
|
142
|
+
it('should use xhr when fetch is unavailable', function(done) {
|
|
143
|
+
var options = {defaultTransport: 'fetch'};
|
|
144
|
+
var oldFetch = window.fetch;
|
|
145
|
+
self.fetch = undefined;
|
|
146
|
+
var t = u.getTransportFromOptions(options, defaults, url);
|
|
147
|
+
expect(t.transport).to.eql('xhr');
|
|
148
|
+
self.fetch = oldFetch;
|
|
149
|
+
done();
|
|
150
|
+
});
|
|
151
|
+
it('should use fetch when xhr is unavailable', function(done) {
|
|
152
|
+
var options = {defaultTransport: 'xhr'};
|
|
153
|
+
var oldXhr = window.XMLHttpRequest;
|
|
154
|
+
self.XMLHttpRequest = undefined;
|
|
155
|
+
var t = u.getTransportFromOptions(options, defaults, url);
|
|
156
|
+
expect(t.transport).to.eql('fetch');
|
|
157
|
+
self.XMLHttpRequest = oldXhr;
|
|
158
|
+
done();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
108
162
|
});
|
|
109
163
|
|
|
110
164
|
describe('transportOptions', function() {
|
|
@@ -1495,17 +1495,21 @@ describe('options.autoInstrument', function() {
|
|
|
1495
1495
|
if(xhr.readyState === 4) {
|
|
1496
1496
|
try {
|
|
1497
1497
|
setTimeout(function() {
|
|
1498
|
-
|
|
1498
|
+
try {
|
|
1499
|
+
server.respond();
|
|
1499
1500
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1501
|
+
expect(server.requests.length).to.eql(2);
|
|
1502
|
+
var body = JSON.parse(server.requests[1].requestBody);
|
|
1502
1503
|
|
|
1503
|
-
|
|
1504
|
+
expect(body.data.body.trace.exception.message).to.eql('HTTP request failed with Status 404');
|
|
1504
1505
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1506
|
+
// Just knowing a stack is present is enough for this test.
|
|
1507
|
+
expect(body.data.body.trace.frames.length).to.be.above(1);
|
|
1507
1508
|
|
|
1508
|
-
|
|
1509
|
+
done();
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
done(e);
|
|
1512
|
+
}
|
|
1509
1513
|
}, 1);
|
|
1510
1514
|
} catch (e) {
|
|
1511
1515
|
done(e);
|
|
@@ -1525,15 +1529,9 @@ describe('options.autoInstrument', function() {
|
|
|
1525
1529
|
|
|
1526
1530
|
window.fetchStub = sinon.stub(window, 'fetch');
|
|
1527
1531
|
|
|
1528
|
-
var
|
|
1529
|
-
start(controller) {
|
|
1530
|
-
controller.enqueue(JSON.stringify({name: 'foo', password: '123456'}));
|
|
1531
|
-
controller.close();
|
|
1532
|
-
}
|
|
1533
|
-
});
|
|
1534
|
-
|
|
1532
|
+
var responseBody = JSON.stringify({name: 'foo', password: '123456'});
|
|
1535
1533
|
window.fetch.returns(Promise.resolve(new Response(
|
|
1536
|
-
|
|
1534
|
+
responseBody,
|
|
1537
1535
|
{ status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'password': '123456' }}
|
|
1538
1536
|
)));
|
|
1539
1537
|
|
|
@@ -1557,11 +1555,18 @@ describe('options.autoInstrument', function() {
|
|
|
1557
1555
|
const fetchInit = {
|
|
1558
1556
|
method: 'POST',
|
|
1559
1557
|
headers: fetchHeaders,
|
|
1560
|
-
body: JSON.stringify({name: 'bar', secret: '
|
|
1558
|
+
body: JSON.stringify({name: 'bar', secret: 'fetch post'})
|
|
1561
1559
|
};
|
|
1562
|
-
var fetchRequest = new Request('https://example.com/
|
|
1560
|
+
var fetchRequest = new Request('https://example.com/fetch-test');
|
|
1563
1561
|
window.fetch(fetchRequest, fetchInit)
|
|
1564
1562
|
.then(function(response) {
|
|
1563
|
+
// Assert that the original stream reader hasn't been read.
|
|
1564
|
+
expect(response.bodyUsed).to.eql(false);
|
|
1565
|
+
return response.text()
|
|
1566
|
+
})
|
|
1567
|
+
.then(function(text) {
|
|
1568
|
+
expect(text).to.eql(responseBody);
|
|
1569
|
+
|
|
1565
1570
|
try {
|
|
1566
1571
|
rollbar.log('test'); // generate a payload to inspect
|
|
1567
1572
|
} catch (e) {
|
|
@@ -1573,8 +1578,9 @@ describe('options.autoInstrument', function() {
|
|
|
1573
1578
|
try {
|
|
1574
1579
|
server.respond();
|
|
1575
1580
|
|
|
1576
|
-
expect(
|
|
1577
|
-
|
|
1581
|
+
expect(window.fetchStub.called).to.be.ok();
|
|
1582
|
+
expect(server.requests.length).to.eql(1);
|
|
1583
|
+
var body = JSON.parse(server.requests[0].requestBody);
|
|
1578
1584
|
|
|
1579
1585
|
// Verify request capture and scrubbing
|
|
1580
1586
|
expect(body.data.body.telemetry[0].body.request).to.eql('{"name":"bar","secret":"********"}');
|
|
@@ -1582,19 +1588,12 @@ describe('options.autoInstrument', function() {
|
|
|
1582
1588
|
// Verify request headers capture and case-insensitive scrubbing
|
|
1583
1589
|
expect(body.data.body.telemetry[0].body.request_headers).to.eql({'content-type': 'application/json', secret: '********'});
|
|
1584
1590
|
|
|
1585
|
-
// When using the Sinon test stub, the response body is populated in Headless Chrome 73,
|
|
1586
|
-
// but not in 77. When using the Fetch API normally, it is populated in all tested Chrome versions.
|
|
1587
|
-
// Disable here due to the Sinon limitation.
|
|
1588
|
-
//
|
|
1589
1591
|
// Verify response capture and scrubbing
|
|
1590
|
-
|
|
1592
|
+
expect(body.data.body.telemetry[0].body.response.body).to.eql('{"name":"foo","password":"********"}');
|
|
1591
1593
|
|
|
1592
1594
|
// Verify response headers capture and case-insensitive scrubbing
|
|
1593
1595
|
expect(body.data.body.telemetry[0].body.response.headers).to.eql({'content-type': 'application/json', password: '********'});
|
|
1594
1596
|
|
|
1595
|
-
// Assert that the original stream reader hasn't been read.
|
|
1596
|
-
expect(response.bodyUsed).to.eql(false);
|
|
1597
|
-
|
|
1598
1597
|
rollbar.configure({ autoInstrument: false });
|
|
1599
1598
|
window.fetch.restore();
|
|
1600
1599
|
done();
|
|
@@ -1606,7 +1605,7 @@ describe('options.autoInstrument', function() {
|
|
|
1606
1605
|
})
|
|
1607
1606
|
});
|
|
1608
1607
|
|
|
1609
|
-
it('should
|
|
1608
|
+
it('should report error for http 4xx fetch calls, when enabled', function(done) {
|
|
1610
1609
|
var server = window.server;
|
|
1611
1610
|
stubResponse(server);
|
|
1612
1611
|
server.requests.length = 0;
|
|
@@ -1659,6 +1658,80 @@ describe('options.autoInstrument', function() {
|
|
|
1659
1658
|
})
|
|
1660
1659
|
});
|
|
1661
1660
|
|
|
1661
|
+
it('should add telemetry headers when fetch Headers object is undefined', function(done) {
|
|
1662
|
+
var server = window.server;
|
|
1663
|
+
stubResponse(server);
|
|
1664
|
+
server.requests.length = 0;
|
|
1665
|
+
|
|
1666
|
+
window.fetchStub = sinon.stub(window, 'fetch');
|
|
1667
|
+
|
|
1668
|
+
var readableStream = new ReadableStream({
|
|
1669
|
+
start(controller) {
|
|
1670
|
+
controller.enqueue(JSON.stringify({name: 'foo', password: '123456'}));
|
|
1671
|
+
controller.close();
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
window.fetch.returns(Promise.resolve(new Response(
|
|
1676
|
+
readableStream,
|
|
1677
|
+
{ status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'password': '123456' }}
|
|
1678
|
+
)));
|
|
1679
|
+
|
|
1680
|
+
var options = {
|
|
1681
|
+
accessToken: 'POST_CLIENT_ITEM_TOKEN',
|
|
1682
|
+
autoInstrument: {
|
|
1683
|
+
log: false,
|
|
1684
|
+
network: true,
|
|
1685
|
+
networkResponseHeaders: true,
|
|
1686
|
+
networkRequestHeaders: true
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
var rollbar = window.rollbar = new Rollbar(options);
|
|
1690
|
+
|
|
1691
|
+
// Remove Headers from window object
|
|
1692
|
+
var originalHeaders = window.Headers;
|
|
1693
|
+
delete window.Headers;
|
|
1694
|
+
|
|
1695
|
+
const fetchInit = {
|
|
1696
|
+
method: 'POST',
|
|
1697
|
+
headers: {'Content-Type': 'application/json', Secret: '123456'},
|
|
1698
|
+
body: JSON.stringify({name: 'bar', secret: 'xhr post'})
|
|
1699
|
+
};
|
|
1700
|
+
var fetchRequest = new Request('https://example.com/xhr-test');
|
|
1701
|
+
window.fetch(fetchRequest, fetchInit)
|
|
1702
|
+
.then(function(response) {
|
|
1703
|
+
try {
|
|
1704
|
+
rollbar.log('test'); // generate a payload to inspect
|
|
1705
|
+
setTimeout(function() {
|
|
1706
|
+
try {
|
|
1707
|
+
server.respond();
|
|
1708
|
+
|
|
1709
|
+
expect(server.requests.length).to.eql(1);
|
|
1710
|
+
var body = JSON.parse(server.requests[0].requestBody);
|
|
1711
|
+
|
|
1712
|
+
// Verify request headers capture and case-insensitive scrubbing
|
|
1713
|
+
expect(body.data.body.telemetry[0].body.request_headers).to.eql({'content-type': 'application/json', secret: '********'});
|
|
1714
|
+
|
|
1715
|
+
// Verify response headers capture and case-insensitive scrubbing
|
|
1716
|
+
expect(body.data.body.telemetry[0].body.response.headers).to.eql({'content-type': 'application/json', password: '********'});
|
|
1717
|
+
|
|
1718
|
+
// Assert that the original stream reader hasn't been read.
|
|
1719
|
+
expect(response.bodyUsed).to.eql(false);
|
|
1720
|
+
|
|
1721
|
+
rollbar.configure({ autoInstrument: false });
|
|
1722
|
+
window.fetch.restore();
|
|
1723
|
+
window.Headers = originalHeaders;
|
|
1724
|
+
done();
|
|
1725
|
+
} catch (e) {
|
|
1726
|
+
done(e);
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
} catch (e) {
|
|
1730
|
+
done(e);
|
|
1731
|
+
}
|
|
1732
|
+
})
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1662
1735
|
it('should add a diagnostic message when wrapConsole fails', function(done) {
|
|
1663
1736
|
var server = window.server;
|
|
1664
1737
|
stubResponse(server);
|
|
@@ -226,9 +226,10 @@ describe('addRequestInfo', function() {
|
|
|
226
226
|
it('should use window info to set request properties', function(done) {
|
|
227
227
|
var args = ['a message'];
|
|
228
228
|
var item = itemFromArgs(args);
|
|
229
|
-
var options = {};
|
|
229
|
+
var options = { captureIp: 'anonymize' };
|
|
230
230
|
t.addRequestInfo(window)(item, options, function(e, i) {
|
|
231
231
|
expect(i.data.request).to.be.ok();
|
|
232
|
+
expect(i.data.request.user_ip).to.eql('$remote_ip_anonymize');
|
|
232
233
|
done(e);
|
|
233
234
|
});
|
|
234
235
|
});
|
|
@@ -243,6 +244,19 @@ describe('addRequestInfo', function() {
|
|
|
243
244
|
done(e);
|
|
244
245
|
});
|
|
245
246
|
});
|
|
247
|
+
it('should honor captureIp without window', function(done) {
|
|
248
|
+
var args = ['a message'];
|
|
249
|
+
var item = itemFromArgs(args);
|
|
250
|
+
item.data = {};
|
|
251
|
+
var options = { captureIp: true };
|
|
252
|
+
var w = null;
|
|
253
|
+
t.addRequestInfo(w)(item, options, function(e, i) {
|
|
254
|
+
expect(i.data.request.url).to.not.be.ok();
|
|
255
|
+
expect(i.data.request.query_string).to.not.be.ok();
|
|
256
|
+
expect(i.data.request.user_ip).to.eql('$remote_ip');
|
|
257
|
+
done(e);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
246
260
|
});
|
|
247
261
|
|
|
248
262
|
describe('addClientInfo', function() {
|
|
@@ -86,6 +86,65 @@ describe('post', function() {
|
|
|
86
86
|
};
|
|
87
87
|
t.post(accessToken, options, payload, callback, requestFactory.getInstance);
|
|
88
88
|
});
|
|
89
|
+
describe('post', function() {
|
|
90
|
+
beforeEach(function (done) {
|
|
91
|
+
window.fetchStub = sinon.stub(window, 'fetch');
|
|
92
|
+
window.server = sinon.createFakeServer();
|
|
93
|
+
done();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterEach(function () {
|
|
97
|
+
window.fetch.restore();
|
|
98
|
+
window.server.restore();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function stubFetchResponse() {
|
|
102
|
+
window.fetch.returns(Promise.resolve(new Response(
|
|
103
|
+
JSON.stringify({ err: 0, message: 'OK', result: { uuid: uuid }}),
|
|
104
|
+
{ status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' }}
|
|
105
|
+
)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function stubXhrResponse() {
|
|
109
|
+
window.server.respondWith(
|
|
110
|
+
[
|
|
111
|
+
200,
|
|
112
|
+
{ 'Content-Type': 'application/json' },
|
|
113
|
+
'{"err": 0, "result":{ "uuid": "d4c7acef55bf4c9ea95e4fe9428a8287"}}'
|
|
114
|
+
]
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
var uuid = 'd4c7acef55bf4c9ea95e4fe9428a8287';
|
|
119
|
+
|
|
120
|
+
it('should use fetch when requested', function(done) {
|
|
121
|
+
var callback = function(err, resp) {
|
|
122
|
+
expect(window.fetchStub.called).to.be.ok();
|
|
123
|
+
expect(server.requests.length).to.eql(0);
|
|
124
|
+
done(err);
|
|
125
|
+
};
|
|
126
|
+
stubFetchResponse();
|
|
127
|
+
stubXhrResponse();
|
|
128
|
+
server.requests.length = 0;
|
|
129
|
+
options.transport = 'fetch';
|
|
130
|
+
t.post(accessToken, options, payload, callback);
|
|
131
|
+
});
|
|
132
|
+
it('should use xhr when requested', function(done) {
|
|
133
|
+
var callback = function(err, resp) {
|
|
134
|
+
expect(window.fetchStub.called).to.not.be.ok();
|
|
135
|
+
expect(server.requests.length).to.eql(1);
|
|
136
|
+
done(err);
|
|
137
|
+
};
|
|
138
|
+
stubFetchResponse();
|
|
139
|
+
stubXhrResponse();
|
|
140
|
+
server.requests.length = 0;
|
|
141
|
+
options.transport = 'xhr';
|
|
142
|
+
t.post(accessToken, options, payload, callback);
|
|
143
|
+
setTimeout(function() {
|
|
144
|
+
server.respond();
|
|
145
|
+
}, 1);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
89
148
|
});
|
|
90
149
|
|
|
91
150
|
var TestRequest = function(response, status, shouldThrowOnSend) {
|