Skip to content

Commit c50429d

Browse files
ajhorstAJ Horst
and
AJ Horst
authoredJul 30, 2021
feat: Add error callback (#413)
* Add error callback to log methods * add logic to call error callbacks on event log failures * add error callback to log identify methods * add tests * refactor dual callback calls * prefix private method with underscore Co-authored-by: AJ Horst <[email protected]>
1 parent 876a4b2 commit c50429d

File tree

2 files changed

+334
-88
lines changed

2 files changed

+334
-88
lines changed
 

‎src/amplitude-client.js

+155-68
Original file line numberDiff line numberDiff line change
@@ -1032,18 +1032,20 @@ var _convertProxyObjectToRealObject = function _convertProxyObjectToRealObject(i
10321032
* @param {Identify} identify_obj - the Identify object containing the user property operations to send.
10331033
* @param {Amplitude~eventCallback} opt_callback - (optional) callback function to run when the identify event has been sent.
10341034
* Note: the server response code and response body from the identify event upload are passed to the callback function.
1035+
* @param {Amplitude~eventCallback} opt_error_callback - (optional) a callback function to run after the event logging
1036+
* fails. The failure can be from the request being malformed or from a network failure
1037+
* Note: the server response code and response body from the event upload are passed to the callback function.
10351038
* @example
10361039
* var identify = new amplitude.Identify().set('colors', ['rose', 'gold']).add('karma', 1).setOnce('sign_up_date', '2016-03-31');
10371040
* amplitude.identify(identify);
10381041
*/
1039-
AmplitudeClient.prototype.identify = function (identify_obj, opt_callback) {
1042+
AmplitudeClient.prototype.identify = function (identify_obj, opt_callback, opt_error_callback) {
10401043
if (this._shouldDeferCall()) {
10411044
return this._q.push(['identify'].concat(Array.prototype.slice.call(arguments, 0)));
10421045
}
10431046
if (!this._apiKeySet('identify()')) {
1044-
if (type(opt_callback) === 'function') {
1045-
opt_callback(0, 'No request sent', { reason: 'API key is not set' });
1046-
}
1047+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', { reason: 'API key is not set' });
1048+
10471049
return;
10481050
}
10491051

@@ -1064,42 +1066,49 @@ AmplitudeClient.prototype.identify = function (identify_obj, opt_callback) {
10641066
null,
10651067
null,
10661068
opt_callback,
1069+
opt_error_callback,
10671070
);
10681071
} else {
1069-
if (type(opt_callback) === 'function') {
1070-
opt_callback(0, 'No request sent', { reason: 'No user property operations' });
1071-
}
1072+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1073+
reason: 'No user property operations',
1074+
});
10721075
}
10731076
} else {
10741077
utils.log.error('Invalid identify input type. Expected Identify object but saw ' + type(identify_obj));
1075-
if (type(opt_callback) === 'function') {
1076-
opt_callback(0, 'No request sent', { reason: 'Invalid identify input type' });
1077-
}
1078+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1079+
reason: 'Invalid identify input type',
1080+
});
10781081
}
10791082
};
10801083

1081-
AmplitudeClient.prototype.groupIdentify = function (group_type, group_name, identify_obj, opt_callback) {
1084+
AmplitudeClient.prototype.groupIdentify = function (
1085+
group_type,
1086+
group_name,
1087+
identify_obj,
1088+
opt_callback,
1089+
opt_error_callback,
1090+
) {
10821091
if (this._shouldDeferCall()) {
10831092
return this._q.push(['groupIdentify'].concat(Array.prototype.slice.call(arguments, 0)));
10841093
}
10851094
if (!this._apiKeySet('groupIdentify()')) {
1086-
if (type(opt_callback) === 'function') {
1087-
opt_callback(0, 'No request sent', { reason: 'API key is not set' });
1088-
}
1095+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1096+
reason: 'API key is not set',
1097+
});
10891098
return;
10901099
}
10911100

10921101
if (!utils.validateInput(group_type, 'group_type', 'string') || utils.isEmptyString(group_type)) {
1093-
if (type(opt_callback) === 'function') {
1094-
opt_callback(0, 'No request sent', { reason: 'Invalid group type' });
1095-
}
1102+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1103+
reason: 'Invalid group type',
1104+
});
10961105
return;
10971106
}
10981107

10991108
if (group_name === null || group_name === undefined) {
1100-
if (type(opt_callback) === 'function') {
1101-
opt_callback(0, 'No request sent', { reason: 'Invalid group name' });
1102-
}
1109+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1110+
reason: 'Invalid group name',
1111+
});
11031112
return;
11041113
}
11051114

@@ -1120,17 +1129,18 @@ AmplitudeClient.prototype.groupIdentify = function (group_type, group_name, iden
11201129
identify_obj.userPropertiesOperations,
11211130
null,
11221131
opt_callback,
1132+
opt_error_callback,
11231133
);
11241134
} else {
1125-
if (type(opt_callback) === 'function') {
1126-
opt_callback(0, 'No request sent', { reason: 'No group property operations' });
1127-
}
1135+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1136+
reason: 'No group property operations',
1137+
});
11281138
}
11291139
} else {
11301140
utils.log.error('Invalid identify input type. Expected Identify object but saw ' + type(identify_obj));
1131-
if (type(opt_callback) === 'function') {
1132-
opt_callback(0, 'No request sent', { reason: 'Invalid identify input type' });
1133-
}
1141+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1142+
reason: 'Invalid identify input type',
1143+
});
11341144
}
11351145
};
11361146

@@ -1164,19 +1174,20 @@ AmplitudeClient.prototype._logEvent = function _logEvent(
11641174
groupProperties,
11651175
timestamp,
11661176
callback,
1177+
errorCallback,
11671178
) {
11681179
_loadCookieData(this); // reload cookie before each log event to sync event meta-data between windows and tabs
11691180

11701181
if (!eventType) {
1171-
if (type(callback) === 'function') {
1172-
callback(0, 'No request sent', { reason: 'Missing eventType' });
1173-
}
1182+
_logErrorsWithCallbacks(callback, errorCallback, 0, 'No request sent', {
1183+
reason: 'Missing eventType',
1184+
});
11741185
return;
11751186
}
11761187
if (this.options.optOut) {
1177-
if (type(callback) === 'function') {
1178-
callback(0, 'No request sent', { reason: 'optOut is set to true' });
1179-
}
1188+
_logErrorsWithCallbacks(callback, errorCallback, 0, 'No request sent', {
1189+
reason: 'optOut is set to true',
1190+
});
11801191
return;
11811192
}
11821193

@@ -1233,10 +1244,10 @@ AmplitudeClient.prototype._logEvent = function _logEvent(
12331244
};
12341245

12351246
if (eventType === Constants.IDENTIFY_EVENT || eventType === Constants.GROUP_IDENTIFY_EVENT) {
1236-
this._unsentIdentifys.push({ event, callback });
1247+
this._unsentIdentifys.push({ event, callback, errorCallback });
12371248
this._limitEventsQueued(this._unsentIdentifys);
12381249
} else {
1239-
this._unsentEvents.push({ event, callback });
1250+
this._unsentEvents.push({ event, callback, errorCallback });
12401251
this._limitEventsQueued(this._unsentEvents);
12411252
}
12421253

@@ -1277,11 +1288,9 @@ AmplitudeClient.prototype._limitEventsQueued = function _limitEventsQueued(queue
12771288
if (queue.length > this.options.savedMaxCount) {
12781289
const deletedEvents = queue.splice(0, queue.length - this.options.savedMaxCount);
12791290
deletedEvents.forEach((event) => {
1280-
if (type(event.callback) === 'function') {
1281-
event.callback(0, 'No request sent', {
1282-
reason: 'Event dropped because options.savedMaxCount exceeded. User may be offline or have a content blocker',
1283-
});
1284-
}
1291+
_logErrorsWithCallbacks(event.callback, event.errorCallback, 0, 'No request sent', {
1292+
reason: 'Event dropped because options.savedMaxCount exceeded. User may be offline or have a content blocker',
1293+
});
12851294
});
12861295
}
12871296
};
@@ -1302,13 +1311,16 @@ AmplitudeClient.prototype._limitEventsQueued = function _limitEventsQueued(queue
13021311
* @param {object} eventProperties - (optional) an object with string keys and values for the event properties.
13031312
* @param {Amplitude~eventCallback} opt_callback - (optional) a callback function to run after the event is logged.
13041313
* Note: the server response code and response body from the event upload are passed to the callback function.
1314+
* @param {Amplitude~eventCallback} opt_error_callback - (optional) a callback function to run after the event logging
1315+
* fails. The failure can be from the request being malformed or from a network failure
1316+
* Note: the server response code and response body from the event upload are passed to the callback function.
13051317
* @example amplitudeClient.logEvent('Clicked Homepage Button', {'finished_flow': false, 'clicks': 15});
13061318
*/
1307-
AmplitudeClient.prototype.logEvent = function logEvent(eventType, eventProperties, opt_callback) {
1319+
AmplitudeClient.prototype.logEvent = function logEvent(eventType, eventProperties, opt_callback, opt_error_callback) {
13081320
if (this._shouldDeferCall()) {
13091321
return this._q.push(['logEvent'].concat(Array.prototype.slice.call(arguments, 0)));
13101322
}
1311-
return this.logEventWithTimestamp(eventType, eventProperties, null, opt_callback);
1323+
return this.logEventWithTimestamp(eventType, eventProperties, null, opt_callback, opt_error_callback);
13121324
};
13131325

13141326
/**
@@ -1319,36 +1331,52 @@ AmplitudeClient.prototype.logEvent = function logEvent(eventType, eventPropertie
13191331
* @param {number} timestamp - (optional) the custom timestamp as milliseconds since epoch.
13201332
* @param {Amplitude~eventCallback} opt_callback - (optional) a callback function to run after the event is logged.
13211333
* Note: the server response code and response body from the event upload are passed to the callback function.
1334+
* @param {Amplitude~eventCallback} opt_error_callback - (optional) a callback function to run after the event logging
1335+
* fails. The failure can be from the request being malformed or from a network failure
1336+
* Note: the server response code and response body from the event upload are passed to the callback function.
13221337
* @example amplitudeClient.logEvent('Clicked Homepage Button', {'finished_flow': false, 'clicks': 15});
13231338
*/
13241339
AmplitudeClient.prototype.logEventWithTimestamp = function logEvent(
13251340
eventType,
13261341
eventProperties,
13271342
timestamp,
13281343
opt_callback,
1344+
opt_error_callback,
13291345
) {
13301346
if (this._shouldDeferCall()) {
13311347
return this._q.push(['logEventWithTimestamp'].concat(Array.prototype.slice.call(arguments, 0)));
13321348
}
13331349
if (!this._apiKeySet('logEvent()')) {
1334-
if (type(opt_callback) === 'function') {
1335-
opt_callback(0, 'No request sent', { reason: 'API key not set' });
1336-
}
1350+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1351+
reason: 'API key not set',
1352+
});
1353+
13371354
return -1;
13381355
}
13391356
if (!utils.validateInput(eventType, 'eventType', 'string')) {
1340-
if (type(opt_callback) === 'function') {
1341-
opt_callback(0, 'No request sent', { reason: 'Invalid type for eventType' });
1342-
}
1357+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1358+
reason: 'Invalid type for eventType',
1359+
});
1360+
13431361
return -1;
13441362
}
13451363
if (utils.isEmptyString(eventType)) {
1346-
if (type(opt_callback) === 'function') {
1347-
opt_callback(0, 'No request sent', { reason: 'Missing eventType' });
1348-
}
1364+
_logErrorsWithCallbacks(opt_callback, opt_error_callback, 0, 'No request sent', {
1365+
reason: 'Missing eventType',
1366+
});
13491367
return -1;
13501368
}
1351-
return this._logEvent(eventType, eventProperties, null, null, null, null, timestamp, opt_callback);
1369+
return this._logEvent(
1370+
eventType,
1371+
eventProperties,
1372+
null,
1373+
null,
1374+
null,
1375+
null,
1376+
timestamp,
1377+
opt_callback,
1378+
opt_error_callback,
1379+
);
13521380
};
13531381

13541382
/**
@@ -1365,25 +1393,35 @@ AmplitudeClient.prototype.logEventWithTimestamp = function logEvent(
13651393
* groupName can be a string or an array of strings.
13661394
* @param {Amplitude~eventCallback} opt_callback - (optional) a callback function to run after the event is logged.
13671395
* Note: the server response code and response body from the event upload are passed to the callback function.
1396+
* @param {Amplitude~eventCallback} opt_error_callback - (optional) a callback function to run after the event logging
1397+
* fails. The failure can be from the request being malformed or from a network failure
1398+
* Note: the server response code and response body from the event upload are passed to the callback function.
13681399
* @example amplitudeClient.logEventWithGroups('Clicked Button', null, {'orgId': 24});
13691400
*/
1370-
AmplitudeClient.prototype.logEventWithGroups = function (eventType, eventProperties, groups, opt_callback) {
1401+
AmplitudeClient.prototype.logEventWithGroups = function (
1402+
eventType,
1403+
eventProperties,
1404+
groups,
1405+
opt_callback,
1406+
opt_error_callback,
1407+
) {
13711408
if (this._shouldDeferCall()) {
13721409
return this._q.push(['logEventWithGroups'].concat(Array.prototype.slice.call(arguments, 0)));
13731410
}
13741411
if (!this._apiKeySet('logEventWithGroups()')) {
1375-
if (type(opt_callback) === 'function') {
1376-
opt_callback(0, 'No request sent', { reason: 'API key not set' });
1377-
}
1412+
_logErrorsWithCallbacks(event.callback, event.errorCallback, 0, 'No request sent', {
1413+
reason: 'API key not set',
1414+
});
1415+
13781416
return -1;
13791417
}
13801418
if (!utils.validateInput(eventType, 'eventType', 'string')) {
1381-
if (type(opt_callback) === 'function') {
1382-
opt_callback(0, 'No request sent', { reason: 'Invalid type for eventType' });
1383-
}
1419+
_logErrorsWithCallbacks(event.callback, event.errorCallback, 0, 'No request sent', {
1420+
reason: 'Invalid type for eventType',
1421+
});
13841422
return -1;
13851423
}
1386-
return this._logEvent(eventType, eventProperties, null, null, groups, null, null, opt_callback);
1424+
return this._logEvent(eventType, eventProperties, null, null, groups, null, null, opt_callback, opt_error_callback);
13871425
};
13881426

13891427
/**
@@ -1394,6 +1432,25 @@ var _isNumber = function _isNumber(n) {
13941432
return !isNaN(parseFloat(n)) && isFinite(n);
13951433
};
13961434

1435+
/**
1436+
* Handles errors that are sent to both callbacks
1437+
* @private
1438+
*/
1439+
var _logErrorsWithCallbacks = function _logErrorsWithCallbacks(
1440+
opt_callback,
1441+
opt_error_callback,
1442+
status,
1443+
response,
1444+
details,
1445+
) {
1446+
if (type(opt_callback) === 'function') {
1447+
opt_callback(status, response, details);
1448+
}
1449+
if (type(opt_error_callback) === 'function') {
1450+
opt_error_callback(status, response, details);
1451+
}
1452+
};
1453+
13971454
/**
13981455
* Log revenue with Revenue interface. The new revenue interface allows for more revenue fields like
13991456
* revenueType and event properties.
@@ -1468,6 +1525,33 @@ if (BUILD_COMPAT_2_0) {
14681525
};
14691526
}
14701527

1528+
/**
1529+
* Calls error callback on unsent events
1530+
* @private
1531+
*/
1532+
AmplitudeClient.prototype._logErrorsOnEvents = function _logErrorsOnEvents(
1533+
maxEventId,
1534+
maxIdentifyId,
1535+
status,
1536+
response,
1537+
) {
1538+
const queues = ['_unsentEvents', '_unsentIdentifys'];
1539+
1540+
for (var j = 0; j < queues.length; j++) {
1541+
const queue = queues[j];
1542+
const maxId = queue === '_unsentEvents' ? maxEventId : maxIdentifyId;
1543+
for (var i = 0; i < this[queue].length || 0; i++) {
1544+
const unsentEvent = this[queue][i];
1545+
1546+
if (unsentEvent.event.event_id <= maxId) {
1547+
if (unsentEvent.errorCallback) {
1548+
unsentEvent.errorCallback(status, response);
1549+
}
1550+
}
1551+
}
1552+
}
1553+
};
1554+
14711555
/**
14721556
* Remove events in storage with event ids up to and including maxEventId.
14731557
* @private
@@ -1565,16 +1649,19 @@ AmplitudeClient.prototype.sendEvents = function sendEvents() {
15651649
scope._sendEventsIfReady();
15661650

15671651
// handle payload too large
1568-
} else if (status === 413) {
1569-
// utils.log('request too large');
1570-
// Can't even get this one massive event through. Drop it, even if it is an identify.
1571-
if (scope.options.uploadBatchSize === 1) {
1572-
scope.removeEvents(maxEventId, maxIdentifyId, status, response);
1652+
} else {
1653+
scope._logErrorsOnEvents(maxEventId, maxIdentifyId, status, response);
1654+
if (status === 413) {
1655+
// utils.log('request too large');
1656+
// Can't even get this one massive event through. Drop it, even if it is an identify.
1657+
if (scope.options.uploadBatchSize === 1) {
1658+
scope.removeEvents(maxEventId, maxIdentifyId, status, response);
1659+
}
1660+
1661+
// The server complained about the length of the request. Backoff and try again.
1662+
scope.options.uploadBatchSize = Math.ceil(numEvents / 2);
1663+
scope.sendEvents();
15731664
}
1574-
1575-
// The server complained about the length of the request. Backoff and try again.
1576-
scope.options.uploadBatchSize = Math.ceil(numEvents / 2);
1577-
scope.sendEvents();
15781665
}
15791666
// else {
15801667
// all the events are still queued, and will be retried when the next

‎test/amplitude-client.js

+179-20
Original file line numberDiff line numberDiff line change
@@ -1216,19 +1216,26 @@ describe('AmplitudeClient', function () {
12161216

12171217
it('should run the callback after making the identify call', function () {
12181218
var counter = 0;
1219+
var errCounter = 0;
12191220
var value = -1;
12201221
var message = '';
12211222
var callback = function (status, response) {
12221223
counter++;
12231224
value = status;
12241225
message = response;
12251226
};
1227+
1228+
var errCallback = function () {
1229+
errCounter++;
1230+
};
1231+
12261232
var identify = new amplitude.Identify().set('key', 'value');
1227-
amplitude.identify(identify, callback);
1233+
amplitude.identify(identify, callback, errCallback);
12281234

12291235
// before server responds, callback should not fire
12301236
assert.lengthOf(server.requests, 1);
12311237
assert.equal(counter, 0);
1238+
assert.equal(errCounter, 0);
12321239
assert.equal(value, -1);
12331240
assert.equal(message, '');
12341241

@@ -1238,41 +1245,76 @@ describe('AmplitudeClient', function () {
12381245
assert.equal(counter, 1);
12391246
assert.equal(value, 200);
12401247
assert.equal(message, 'success');
1248+
1249+
// error callback should not fire
1250+
assert.equal(errCounter, 0);
12411251
});
12421252

12431253
it('should run the callback even if client not initialized with apiKey', function () {
12441254
var counter = 0;
12451255
var value = -1;
12461256
var message = '';
1257+
1258+
var errCounter = 0;
1259+
var errValue = -1;
1260+
var errMessage = '';
12471261
var callback = function (status, response) {
12481262
counter++;
12491263
value = status;
12501264
message = response;
12511265
};
1266+
1267+
var errCallback = function (status, response) {
1268+
errCounter++;
1269+
errValue = status;
1270+
errMessage = response;
1271+
};
1272+
12521273
var identify = new amplitude.Identify().set('key', 'value');
1253-
new AmplitudeClient().identify(identify, callback);
1274+
new AmplitudeClient().identify(identify, callback, errCallback);
12541275

12551276
// verify callback fired
12561277
assert.equal(counter, 1);
12571278
assert.equal(value, 0);
12581279
assert.equal(message, 'No request sent');
1280+
1281+
// verify error callback fired
1282+
assert.equal(errCounter, 1);
1283+
assert.equal(errValue, 0);
1284+
assert.equal(errMessage, 'No request sent');
12591285
});
12601286

12611287
it('should run the callback even with an invalid identify object', function () {
12621288
var counter = 0;
12631289
var value = -1;
12641290
var message = '';
1291+
1292+
var errCounter = 0;
1293+
var errValue = -1;
1294+
var errMessage = '';
12651295
var callback = function (status, response) {
12661296
counter++;
12671297
value = status;
12681298
message = response;
12691299
};
1270-
amplitude.identify(null, callback);
1300+
1301+
var errCallback = function (status, response) {
1302+
errCounter++;
1303+
errValue = status;
1304+
errMessage = response;
1305+
};
1306+
1307+
amplitude.identify(null, callback, errCallback);
12711308

12721309
// verify callback fired
12731310
assert.equal(counter, 1);
12741311
assert.equal(value, 0);
12751312
assert.equal(message, 'No request sent');
1313+
1314+
// verify error callback fired
1315+
assert.equal(errCounter, 1);
1316+
assert.equal(errValue, 0);
1317+
assert.equal(errMessage, 'No request sent');
12761318
});
12771319
});
12781320

@@ -1390,19 +1432,24 @@ describe('AmplitudeClient', function () {
13901432

13911433
it('should run the callback after making the identify call', function () {
13921434
var counter = 0;
1435+
var errCounter = 0;
13931436
var value = -1;
13941437
var message = '';
13951438
var callback = function (status, response) {
13961439
counter++;
13971440
value = status;
13981441
message = response;
13991442
};
1443+
var errCallback = function () {
1444+
errCounter++;
1445+
};
14001446
var identify = new amplitude.Identify().set('key', 'value');
1401-
amplitude.groupIdentify(group_type, group_name, identify, callback);
1447+
amplitude.groupIdentify(group_type, group_name, identify, callback, errCallback);
14021448

14031449
// before server responds, callback should not fire
14041450
assert.lengthOf(server.requests, 1);
14051451
assert.equal(counter, 0);
1452+
assert.equal(errCounter, 0);
14061453
assert.equal(value, -1);
14071454
assert.equal(message, '');
14081455

@@ -1412,24 +1459,42 @@ describe('AmplitudeClient', function () {
14121459
assert.equal(counter, 1);
14131460
assert.equal(value, 200);
14141461
assert.equal(message, 'success');
1462+
1463+
// error callback should not be fired
1464+
assert.equal(errCounter, 0);
14151465
});
14161466

14171467
it('should run the callback even if client not initialized with apiKey', function () {
14181468
var counter = 0;
14191469
var value = -1;
14201470
var message = '';
1471+
1472+
var errCounter = 0;
1473+
var errValue = -1;
1474+
var errMessage = '';
14211475
var callback = function (status, response) {
14221476
counter++;
14231477
value = status;
14241478
message = response;
14251479
};
1480+
1481+
var errCallback = function (status, response) {
1482+
errCounter++;
1483+
errValue = status;
1484+
errMessage = response;
1485+
};
14261486
var identify = new amplitude.Identify().set('key', 'value');
1427-
new AmplitudeClient().groupIdentify(group_type, group_name, identify, callback);
1487+
new AmplitudeClient().groupIdentify(group_type, group_name, identify, callback, errCallback);
14281488

14291489
// verify callback fired
14301490
assert.equal(counter, 1);
14311491
assert.equal(value, 0);
14321492
assert.equal(message, 'No request sent');
1493+
1494+
// verify error callback fired
1495+
assert.equal(errCounter, 1);
1496+
assert.equal(errValue, 0);
1497+
assert.equal(errMessage, 'No request sent');
14331498
});
14341499

14351500
it('should run the callback even with an invalid identify object', function () {
@@ -1894,31 +1959,57 @@ describe('AmplitudeClient', function () {
18941959
var counter = 0;
18951960
var value = -1;
18961961
var message = '';
1962+
1963+
var errCounter = 0;
1964+
var errValue = -1;
1965+
var errMessage = '';
18971966
var callback = function (status, response) {
18981967
counter++;
18991968
value = status;
19001969
message = response;
19011970
};
1902-
amplitude.logEvent(null, null, callback);
1971+
1972+
var errCallback = function (status, response) {
1973+
errCounter++;
1974+
errValue = status;
1975+
errMessage = response;
1976+
};
1977+
amplitude.logEvent(null, null, callback, errCallback);
19031978
assert.equal(counter, 1);
19041979
assert.equal(value, 0);
19051980
assert.equal(message, 'No request sent');
1981+
assert.equal(errCounter, 1);
1982+
assert.equal(errValue, 0);
1983+
assert.equal(errMessage, 'No request sent');
19061984
});
19071985

19081986
it('should run callback if optout', function () {
19091987
amplitude.setOptOut(true);
19101988
var counter = 0;
19111989
var value = -1;
19121990
var message = '';
1991+
1992+
var errCounter = 0;
1993+
var errValue = -1;
1994+
var errMessage = '';
19131995
var callback = function (status, response) {
19141996
counter++;
19151997
value = status;
19161998
message = response;
19171999
};
1918-
amplitude.logEvent('test', null, callback);
2000+
2001+
var errCallback = function (status, response) {
2002+
errCounter++;
2003+
errValue = status;
2004+
errMessage = response;
2005+
};
2006+
amplitude.logEvent('test', null, callback, errCallback);
19192007
assert.equal(counter, 1);
19202008
assert.equal(value, 0);
19212009
assert.equal(message, 'No request sent');
2010+
assert.equal(errCounter, 1);
2011+
assert.equal(errValue, 0);
2012+
assert.equal(errMessage, 'No request sent');
19222013
});
19232014

19242015
it('should not run callback if invalid callback and no eventType', function () {
@@ -1927,18 +2018,25 @@ describe('AmplitudeClient', function () {
19272018

19282019
it('should run callback after logging event', function () {
19292020
var counter = 0;
2021+
var errCounter = 0;
19302022
var value = -1;
19312023
var message = '';
2024+
2025+
var errCallback = function () {
2026+
errCounter++;
2027+
};
2028+
19322029
var callback = function (status, response) {
19332030
counter++;
19342031
value = status;
19352032
message = response;
19362033
};
1937-
amplitude.logEvent('test', null, callback);
2034+
amplitude.logEvent('test', null, callback, errCallback);
19382035

19392036
// before server responds, callback should not fire
19402037
assert.lengthOf(server.requests, 1);
19412038
assert.equal(counter, 0);
2039+
assert.equal(errCounter, 0);
19422040
assert.equal(value, -1);
19432041
assert.equal(message, '');
19442042

@@ -1948,6 +2046,9 @@ describe('AmplitudeClient', function () {
19482046
assert.equal(counter, 1);
19492047
assert.equal(value, 200);
19502048
assert.equal(message, 'success');
2049+
2050+
// erro callback should not fire
2051+
assert.equal(errCounter, 0);
19512052
});
19522053

19532054
it('should run callback if batchEvents but under threshold', function () {
@@ -1958,14 +2059,20 @@ describe('AmplitudeClient', function () {
19582059
eventUploadPeriodMillis: eventUploadPeriodMillis,
19592060
});
19602061
var counter = 0;
2062+
var errCounter = 0;
19612063
var value = -1;
19622064
var message = '';
19632065
var callback = function (status, response) {
19642066
counter++;
19652067
value = status;
19662068
message = response;
19672069
};
1968-
amplitude.logEvent('test', null, callback);
2070+
2071+
var errCallback = function () {
2072+
errCounter++;
2073+
};
2074+
2075+
amplitude.logEvent('test', null, callback, errCallback);
19692076
assert.lengthOf(server.requests, 0);
19702077

19712078
// check that request is made after delay, but callback is not run a second time
@@ -1974,13 +2081,15 @@ describe('AmplitudeClient', function () {
19742081
server.respondWith('success');
19752082
server.respond();
19762083
assert.equal(counter, 1);
2084+
assert.equal(errCounter, 0);
19772085
assert.equal(value, 200);
19782086
assert.equal(message, 'success');
19792087
});
19802088

19812089
it('should run callback once and only after all events are uploaded', function () {
19822090
amplitude.init(apiKey, null, { uploadBatchSize: 10 });
19832091
var counter = 0;
2092+
var errCounter = 0;
19842093
var value = -1;
19852094
var message = '';
19862095
var callback = function (status, response) {
@@ -1989,21 +2098,26 @@ describe('AmplitudeClient', function () {
19892098
message = response;
19902099
};
19912100

2101+
var errCallback = function () {
2102+
errCounter++;
2103+
};
2104+
19922105
// queue up 15 events, since batchsize 10, need to send in 2 batches
19932106
amplitude._sending = true;
19942107
for (var i = 0; i < 15; i++) {
19952108
amplitude.logEvent('Event', { index: i });
19962109
}
19972110
amplitude._sending = false;
19982111

1999-
amplitude.logEvent('Event', { index: 100 }, callback);
2112+
amplitude.logEvent('Event', { index: 100 }, callback, errCallback);
20002113

20012114
assert.lengthOf(server.requests, 1);
20022115
server.respondWith('success');
20032116
server.respond();
20042117

20052118
// after first response received, callback should not have fired
20062119
assert.equal(counter, 0);
2120+
assert.equal(errCounter, 0);
20072121
assert.equal(value, -1);
20082122
assert.equal(message, '');
20092123

@@ -2015,22 +2129,37 @@ describe('AmplitudeClient', function () {
20152129
assert.equal(counter, 1);
20162130
assert.equal(value, 200);
20172131
assert.equal(message, 'success');
2132+
2133+
// error callback should not have fired
2134+
assert.equal(errCounter, 0);
20182135
});
20192136

20202137
it('should run the callback even with a dropped unsent event', function () {
20212138
amplitude.init(apiKey, null, { savedMaxCount: 1 });
20222139
var counter = 0;
2023-
var value = null;
2024-
var message = null;
2025-
var reason = null;
2140+
var value = -1;
2141+
var message = '';
2142+
var reason = '';
2143+
2144+
var errCounter = 0;
2145+
var errValue = -1;
2146+
var errMessage = '';
2147+
var errReason = '';
20262148
var callback = function (status, response, details) {
20272149
counter++;
20282150
value = status;
20292151
message = response;
20302152
reason = details.reason;
20312153
};
2032-
amplitude.logEvent('DroppedEvent', {}, callback);
2033-
amplitude.logEvent('SavedEvent', {}, callback);
2154+
2155+
var errCallback = function (status, response, details) {
2156+
errCounter++;
2157+
errValue = status;
2158+
errMessage = response;
2159+
errReason = details.reason;
2160+
};
2161+
amplitude.logEvent('DroppedEvent', {}, callback, errCallback);
2162+
amplitude.logEvent('SavedEvent', {}, callback, errCallback);
20342163
server.respondWith([0, {}, '']);
20352164
server.respond();
20362165

@@ -2042,10 +2171,20 @@ describe('AmplitudeClient', function () {
20422171
reason,
20432172
'Event dropped because options.savedMaxCount exceeded. User may be offline or have a content blocker',
20442173
);
2174+
2175+
// verify error callback fired
2176+
assert.equal(errCounter, 1);
2177+
assert.equal(errValue, 0);
2178+
assert.equal(errMessage, 'No request sent');
2179+
assert.equal(
2180+
errReason,
2181+
'Event dropped because options.savedMaxCount exceeded. User may be offline or have a content blocker',
2182+
);
20452183
});
20462184

20472185
it('should run callback once and only after 413 resolved', function () {
20482186
var counter = 0;
2187+
var errCounter = 0;
20492188
var value = -1;
20502189
var message = '';
20512190
var callback = function (status, response) {
@@ -2054,6 +2193,10 @@ describe('AmplitudeClient', function () {
20542193
message = response;
20552194
};
20562195

2196+
var errCallback = function () {
2197+
errCounter++;
2198+
};
2199+
20572200
// queue up 15 events
20582201
amplitude._sending = true;
20592202
for (var i = 0; i < 15; i++) {
@@ -2062,7 +2205,7 @@ describe('AmplitudeClient', function () {
20622205
amplitude._sending = false;
20632206

20642207
// 16th event with 413 will backoff to batches of 8
2065-
amplitude.logEvent('Event', { index: 100 }, callback);
2208+
amplitude.logEvent('Event', { index: 100 }, callback, errCallback);
20662209

20672210
assert.lengthOf(server.requests, 1);
20682211
var events = JSON.parse(queryString.parse(server.requests[0].requestBody).e);
@@ -2072,6 +2215,7 @@ describe('AmplitudeClient', function () {
20722215
server.respondWith([413, {}, '']);
20732216
server.respond();
20742217
assert.equal(counter, 0);
2218+
assert.equal(errCounter, 1);
20752219
assert.equal(value, -1);
20762220
assert.equal(message, '');
20772221

@@ -2082,6 +2226,7 @@ describe('AmplitudeClient', function () {
20822226
server.respondWith('success');
20832227
server.respond();
20842228
assert.equal(counter, 0);
2229+
assert.equal(errCounter, 1);
20852230
assert.equal(value, -1);
20862231
assert.equal(message, '');
20872232

@@ -2092,25 +2237,30 @@ describe('AmplitudeClient', function () {
20922237
server.respondWith('success');
20932238
server.respond();
20942239
assert.equal(counter, 1);
2240+
assert.equal(errCounter, 1);
20952241
assert.equal(value, 200);
20962242
assert.equal(message, 'success');
20972243
});
20982244

20992245
it('should _not_ run callback when the server returns a 500', function () {
21002246
const callback = sinon.spy();
2247+
const errCallback = sinon.spy();
21012248

2102-
amplitude.logEvent('test', null, callback);
2249+
amplitude.logEvent('test', null, callback, errCallback);
21032250
server.respondWith([500, {}, 'Not found']);
21042251
server.respond();
21052252
assert.isFalse(callback.calledOnce);
2253+
assert.isTrue(errCallback.calledOnce);
21062254
});
21072255

21082256
it('should run the callback when the server finally returns a 200 after a 500', function () {
21092257
const callback = sinon.spy();
2258+
const errCallback = sinon.spy();
21102259

2111-
amplitude.logEvent('test', null, callback);
2260+
amplitude.logEvent('test', null, callback, errCallback);
21122261
server.respondWith([500, {}, 'Not found']);
21132262
server.respond();
2263+
assert.isTrue(errCallback.calledOnce);
21142264
// The SDK retries failed events when a new event is sent
21152265
amplitude.logEvent('test2');
21162266
server.respondWith([200, {}, 'success']);
@@ -2121,10 +2271,12 @@ describe('AmplitudeClient', function () {
21212271

21222272
it('should run the callback when the server finally returns a 413 after a 500', function () {
21232273
const callback = sinon.spy();
2274+
const errCallback = sinon.spy();
21242275

2125-
amplitude.logEvent('test', null, callback);
2276+
amplitude.logEvent('test', null, callback, errCallback);
21262277
server.respondWith([500, {}, 'Not found']);
21272278
server.respond();
2279+
assert.isTrue(errCallback.calledOnce);
21282280
// The SDK retries failed events when a new event is sent
21292281
amplitude.logEvent('test2');
21302282
server.respondWith([413, {}, '']);
@@ -2562,6 +2714,7 @@ describe('AmplitudeClient', function () {
25622714

25632715
it('should handle groups input', function () {
25642716
var counter = 0;
2717+
var errCounter = 0;
25652718
var value = -1;
25662719
var message = '';
25672720
var callback = function (status, response) {
@@ -2571,6 +2724,10 @@ describe('AmplitudeClient', function () {
25712724
message = response;
25722725
};
25732726

2727+
var errCallback = function () {
2728+
errCounter++;
2729+
};
2730+
25742731
var eventProperties = {
25752732
key: 'value',
25762733
};
@@ -2582,7 +2739,7 @@ describe('AmplitudeClient', function () {
25822739
null: null, // ignore null values
25832740
};
25842741

2585-
amplitude.logEventWithGroups('Test', eventProperties, groups, callback);
2742+
amplitude.logEventWithGroups('Test', eventProperties, groups, callback, errCallback);
25862743
assert.lengthOf(server.requests, 1);
25872744
var events = JSON.parse(queryString.parse(server.requests[0].requestBody).e);
25882745
assert.lengthOf(events, 1);
@@ -2600,11 +2757,13 @@ describe('AmplitudeClient', function () {
26002757

26012758
// verify callback behavior
26022759
assert.equal(counter, 0);
2760+
assert.equal(errCounter, 0);
26032761
assert.equal(value, -1);
26042762
assert.equal(message, '');
26052763
server.respondWith('success');
26062764
server.respond();
26072765
assert.equal(counter, 1);
2766+
assert.equal(errCounter, 0);
26082767
assert.equal(value, 200);
26092768
assert.equal(message, 'success');
26102769
});

0 commit comments

Comments
 (0)
Please sign in to comment.