@@ -1032,18 +1032,20 @@ var _convertProxyObjectToRealObject = function _convertProxyObjectToRealObject(i
1032
1032
* @param {Identify } identify_obj - the Identify object containing the user property operations to send.
1033
1033
* @param {Amplitude~eventCallback } opt_callback - (optional) callback function to run when the identify event has been sent.
1034
1034
* 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.
1035
1038
* @example
1036
1039
* var identify = new amplitude.Identify().set('colors', ['rose', 'gold']).add('karma', 1).setOnce('sign_up_date', '2016-03-31');
1037
1040
* amplitude.identify(identify);
1038
1041
*/
1039
- AmplitudeClient . prototype . identify = function ( identify_obj , opt_callback ) {
1042
+ AmplitudeClient . prototype . identify = function ( identify_obj , opt_callback , opt_error_callback ) {
1040
1043
if ( this . _shouldDeferCall ( ) ) {
1041
1044
return this . _q . push ( [ 'identify' ] . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ) ;
1042
1045
}
1043
1046
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
+
1047
1049
return ;
1048
1050
}
1049
1051
@@ -1064,42 +1066,49 @@ AmplitudeClient.prototype.identify = function (identify_obj, opt_callback) {
1064
1066
null ,
1065
1067
null ,
1066
1068
opt_callback ,
1069
+ opt_error_callback ,
1067
1070
) ;
1068
1071
} 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
+ } ) ;
1072
1075
}
1073
1076
} else {
1074
1077
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
+ } ) ;
1078
1081
}
1079
1082
} ;
1080
1083
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
+ ) {
1082
1091
if ( this . _shouldDeferCall ( ) ) {
1083
1092
return this . _q . push ( [ 'groupIdentify' ] . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ) ;
1084
1093
}
1085
1094
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
+ } ) ;
1089
1098
return ;
1090
1099
}
1091
1100
1092
1101
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
+ } ) ;
1096
1105
return ;
1097
1106
}
1098
1107
1099
1108
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
+ } ) ;
1103
1112
return ;
1104
1113
}
1105
1114
@@ -1120,17 +1129,18 @@ AmplitudeClient.prototype.groupIdentify = function (group_type, group_name, iden
1120
1129
identify_obj . userPropertiesOperations ,
1121
1130
null ,
1122
1131
opt_callback ,
1132
+ opt_error_callback ,
1123
1133
) ;
1124
1134
} 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
+ } ) ;
1128
1138
}
1129
1139
} else {
1130
1140
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
+ } ) ;
1134
1144
}
1135
1145
} ;
1136
1146
@@ -1164,19 +1174,20 @@ AmplitudeClient.prototype._logEvent = function _logEvent(
1164
1174
groupProperties ,
1165
1175
timestamp ,
1166
1176
callback ,
1177
+ errorCallback ,
1167
1178
) {
1168
1179
_loadCookieData ( this ) ; // reload cookie before each log event to sync event meta-data between windows and tabs
1169
1180
1170
1181
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
+ } ) ;
1174
1185
return ;
1175
1186
}
1176
1187
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
+ } ) ;
1180
1191
return ;
1181
1192
}
1182
1193
@@ -1233,10 +1244,10 @@ AmplitudeClient.prototype._logEvent = function _logEvent(
1233
1244
} ;
1234
1245
1235
1246
if ( eventType === Constants . IDENTIFY_EVENT || eventType === Constants . GROUP_IDENTIFY_EVENT ) {
1236
- this . _unsentIdentifys . push ( { event, callback } ) ;
1247
+ this . _unsentIdentifys . push ( { event, callback, errorCallback } ) ;
1237
1248
this . _limitEventsQueued ( this . _unsentIdentifys ) ;
1238
1249
} else {
1239
- this . _unsentEvents . push ( { event, callback } ) ;
1250
+ this . _unsentEvents . push ( { event, callback, errorCallback } ) ;
1240
1251
this . _limitEventsQueued ( this . _unsentEvents ) ;
1241
1252
}
1242
1253
@@ -1277,11 +1288,9 @@ AmplitudeClient.prototype._limitEventsQueued = function _limitEventsQueued(queue
1277
1288
if ( queue . length > this . options . savedMaxCount ) {
1278
1289
const deletedEvents = queue . splice ( 0 , queue . length - this . options . savedMaxCount ) ;
1279
1290
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
+ } ) ;
1285
1294
} ) ;
1286
1295
}
1287
1296
} ;
@@ -1302,13 +1311,16 @@ AmplitudeClient.prototype._limitEventsQueued = function _limitEventsQueued(queue
1302
1311
* @param {object } eventProperties - (optional) an object with string keys and values for the event properties.
1303
1312
* @param {Amplitude~eventCallback } opt_callback - (optional) a callback function to run after the event is logged.
1304
1313
* 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.
1305
1317
* @example amplitudeClient.logEvent('Clicked Homepage Button', {'finished_flow': false, 'clicks': 15});
1306
1318
*/
1307
- AmplitudeClient . prototype . logEvent = function logEvent ( eventType , eventProperties , opt_callback ) {
1319
+ AmplitudeClient . prototype . logEvent = function logEvent ( eventType , eventProperties , opt_callback , opt_error_callback ) {
1308
1320
if ( this . _shouldDeferCall ( ) ) {
1309
1321
return this . _q . push ( [ 'logEvent' ] . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ) ;
1310
1322
}
1311
- return this . logEventWithTimestamp ( eventType , eventProperties , null , opt_callback ) ;
1323
+ return this . logEventWithTimestamp ( eventType , eventProperties , null , opt_callback , opt_error_callback ) ;
1312
1324
} ;
1313
1325
1314
1326
/**
@@ -1319,36 +1331,52 @@ AmplitudeClient.prototype.logEvent = function logEvent(eventType, eventPropertie
1319
1331
* @param {number } timestamp - (optional) the custom timestamp as milliseconds since epoch.
1320
1332
* @param {Amplitude~eventCallback } opt_callback - (optional) a callback function to run after the event is logged.
1321
1333
* 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.
1322
1337
* @example amplitudeClient.logEvent('Clicked Homepage Button', {'finished_flow': false, 'clicks': 15});
1323
1338
*/
1324
1339
AmplitudeClient . prototype . logEventWithTimestamp = function logEvent (
1325
1340
eventType ,
1326
1341
eventProperties ,
1327
1342
timestamp ,
1328
1343
opt_callback ,
1344
+ opt_error_callback ,
1329
1345
) {
1330
1346
if ( this . _shouldDeferCall ( ) ) {
1331
1347
return this . _q . push ( [ 'logEventWithTimestamp' ] . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ) ;
1332
1348
}
1333
1349
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
+
1337
1354
return - 1 ;
1338
1355
}
1339
1356
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
+
1343
1361
return - 1 ;
1344
1362
}
1345
1363
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
+ } ) ;
1349
1367
return - 1 ;
1350
1368
}
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
+ ) ;
1352
1380
} ;
1353
1381
1354
1382
/**
@@ -1365,25 +1393,35 @@ AmplitudeClient.prototype.logEventWithTimestamp = function logEvent(
1365
1393
* groupName can be a string or an array of strings.
1366
1394
* @param {Amplitude~eventCallback } opt_callback - (optional) a callback function to run after the event is logged.
1367
1395
* 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.
1368
1399
* @example amplitudeClient.logEventWithGroups('Clicked Button', null, {'orgId': 24});
1369
1400
*/
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
+ ) {
1371
1408
if ( this . _shouldDeferCall ( ) ) {
1372
1409
return this . _q . push ( [ 'logEventWithGroups' ] . concat ( Array . prototype . slice . call ( arguments , 0 ) ) ) ;
1373
1410
}
1374
1411
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
+
1378
1416
return - 1 ;
1379
1417
}
1380
1418
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
+ } ) ;
1384
1422
return - 1 ;
1385
1423
}
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 ) ;
1387
1425
} ;
1388
1426
1389
1427
/**
@@ -1394,6 +1432,25 @@ var _isNumber = function _isNumber(n) {
1394
1432
return ! isNaN ( parseFloat ( n ) ) && isFinite ( n ) ;
1395
1433
} ;
1396
1434
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
+
1397
1454
/**
1398
1455
* Log revenue with Revenue interface. The new revenue interface allows for more revenue fields like
1399
1456
* revenueType and event properties.
@@ -1468,6 +1525,33 @@ if (BUILD_COMPAT_2_0) {
1468
1525
} ;
1469
1526
}
1470
1527
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
+
1471
1555
/**
1472
1556
* Remove events in storage with event ids up to and including maxEventId.
1473
1557
* @private
@@ -1565,16 +1649,19 @@ AmplitudeClient.prototype.sendEvents = function sendEvents() {
1565
1649
scope . _sendEventsIfReady ( ) ;
1566
1650
1567
1651
// 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 ( ) ;
1573
1664
}
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 ( ) ;
1578
1665
}
1579
1666
// else {
1580
1667
// all the events are still queued, and will be retried when the next
0 commit comments