@atlaskit/editor-synced-block-provider 8.1.7 → 8.1.9
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/CHANGELOG.md +14 -0
- package/dist/cjs/providers/block-service/blockServiceAPI.js +16 -8
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +4 -2
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +15 -8
- package/dist/cjs/utils/errorHandling.js +65 -11
- package/dist/es2019/providers/block-service/blockServiceAPI.js +16 -8
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +5 -3
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +14 -7
- package/dist/es2019/utils/errorHandling.js +62 -5
- package/dist/esm/providers/block-service/blockServiceAPI.js +16 -8
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +5 -3
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +16 -9
- package/dist/esm/utils/errorHandling.js +64 -10
- package/dist/types/providers/types.d.ts +18 -0
- package/dist/types/utils/errorHandling.d.ts +44 -5
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 8.1.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`a4e834c2647ac`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a4e834c2647ac) -
|
|
8
|
+
Add failure reason and HTTP status code attributes to synced block delete, update and create
|
|
9
|
+
operational error analytics, so failures can be broken down by cause.
|
|
10
|
+
|
|
11
|
+
## 8.1.8
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
|
|
3
17
|
## 8.1.7
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -574,7 +574,8 @@ var writeDataBatch = exports.writeDataBatch = /*#__PURE__*/function () {
|
|
|
574
574
|
return _context4.abrupt("return", data.map(function (block) {
|
|
575
575
|
return {
|
|
576
576
|
error: mapBlockError(_t6),
|
|
577
|
-
resourceId: block.resourceId
|
|
577
|
+
resourceId: block.resourceId,
|
|
578
|
+
statusCode: _t6.status
|
|
578
579
|
};
|
|
579
580
|
}));
|
|
580
581
|
case 11:
|
|
@@ -922,7 +923,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
922
923
|
}
|
|
923
924
|
return _context8.abrupt("return", {
|
|
924
925
|
error: mapBlockError(_t0),
|
|
925
|
-
resourceId: resourceId
|
|
926
|
+
resourceId: resourceId,
|
|
927
|
+
statusCode: _t0.status
|
|
926
928
|
});
|
|
927
929
|
case 8:
|
|
928
930
|
return _context8.abrupt("return", {
|
|
@@ -1002,7 +1004,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1002
1004
|
}
|
|
1003
1005
|
return _context9.abrupt("return", {
|
|
1004
1006
|
error: mapBlockError(_t10),
|
|
1005
|
-
resourceId: resourceId
|
|
1007
|
+
resourceId: resourceId,
|
|
1008
|
+
statusCode: _t10.status
|
|
1006
1009
|
});
|
|
1007
1010
|
case 8:
|
|
1008
1011
|
return _context9.abrupt("return", {
|
|
@@ -1086,7 +1089,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1086
1089
|
return _context0.abrupt("return", {
|
|
1087
1090
|
resourceId: resourceId,
|
|
1088
1091
|
success: false,
|
|
1089
|
-
error: mapBlockError(_t11)
|
|
1092
|
+
error: mapBlockError(_t11),
|
|
1093
|
+
statusCode: _t11.status
|
|
1090
1094
|
});
|
|
1091
1095
|
case 7:
|
|
1092
1096
|
return _context0.abrupt("return", {
|
|
@@ -1161,7 +1165,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1161
1165
|
return _context1.abrupt("return", {
|
|
1162
1166
|
resourceId: resourceId,
|
|
1163
1167
|
success: false,
|
|
1164
|
-
error: mapBlockError(_t13)
|
|
1168
|
+
error: mapBlockError(_t13),
|
|
1169
|
+
statusCode: _t13.status
|
|
1165
1170
|
});
|
|
1166
1171
|
case 9:
|
|
1167
1172
|
return _context1.abrupt("return", {
|
|
@@ -1191,7 +1196,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1191
1196
|
return _context1.abrupt("return", {
|
|
1192
1197
|
resourceId: resourceId,
|
|
1193
1198
|
success: false,
|
|
1194
|
-
error: mapBlockError(_t14)
|
|
1199
|
+
error: mapBlockError(_t14),
|
|
1200
|
+
statusCode: _t14.status
|
|
1195
1201
|
});
|
|
1196
1202
|
case 13:
|
|
1197
1203
|
return _context1.abrupt("return", {
|
|
@@ -1276,7 +1282,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1276
1282
|
}
|
|
1277
1283
|
return _context10.abrupt("return", {
|
|
1278
1284
|
success: false,
|
|
1279
|
-
error: mapBlockError(_t15)
|
|
1285
|
+
error: mapBlockError(_t15),
|
|
1286
|
+
statusCode: _t15.status
|
|
1280
1287
|
});
|
|
1281
1288
|
case 4:
|
|
1282
1289
|
return _context10.abrupt("return", {
|
|
@@ -1443,7 +1450,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1443
1450
|
return _context12.abrupt("return", data.map(function (block) {
|
|
1444
1451
|
return {
|
|
1445
1452
|
error: mapBlockError(_t18),
|
|
1446
|
-
resourceId: block.resourceId
|
|
1453
|
+
resourceId: block.resourceId,
|
|
1454
|
+
statusCode: _t18.status
|
|
1447
1455
|
};
|
|
1448
1456
|
}));
|
|
1449
1457
|
case 15:
|
|
@@ -1084,7 +1084,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1084
1084
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
1085
1085
|
reason: updateResult.error || 'Failed to update reference synced blocks on the document'
|
|
1086
1086
|
});
|
|
1087
|
-
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, (0, _errorHandling.updateReferenceErrorPayload)(updateResult.error || 'Failed to update reference synced blocks on the document'));
|
|
1087
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, (0, _errorHandling.updateReferenceErrorPayload)(updateResult.error || 'Failed to update reference synced blocks on the document', undefined, undefined, (0, _errorHandling.buildErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), updateResult.error, updateResult.statusCode)));
|
|
1088
1088
|
}
|
|
1089
1089
|
_context4.next = 17;
|
|
1090
1090
|
break;
|
|
@@ -1099,7 +1099,9 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
1099
1099
|
reason: _t3.message
|
|
1100
1100
|
});
|
|
1101
1101
|
// No `resourceId` available in this catch — sourceProduct is intentionally omitted.
|
|
1102
|
-
|
|
1102
|
+
// No structured SyncBlockError/status here, so the attribution `reason` falls back
|
|
1103
|
+
// to `unknown` when the gate is on.
|
|
1104
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, (0, _errorHandling.updateReferenceErrorPayload)(_t3.message, undefined, undefined, (0, _errorHandling.buildErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'))));
|
|
1103
1105
|
case 17:
|
|
1104
1106
|
_context4.prev = 17;
|
|
1105
1107
|
if (!success) {
|
|
@@ -12,6 +12,7 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
|
|
|
12
12
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
13
13
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
14
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
15
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
15
16
|
var _types = require("../common/types");
|
|
16
17
|
var _errorHandling = require("../utils/errorHandling");
|
|
17
18
|
var _experienceTracking = require("../utils/experienceTracking");
|
|
@@ -165,7 +166,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
165
166
|
value: (function () {
|
|
166
167
|
var _flush = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
167
168
|
var _this2 = this;
|
|
168
|
-
var _this$saveExperience, timedOut, timeoutId, timeout, _iterator, _step, resourceId, bodiedSyncBlockNodes, bodiedSyncBlockData, writeResults, _this$saveExperience2, _this$saveExperience3, _this$fireAnalyticsEv2, _this$flushCompletion, _t;
|
|
169
|
+
var _this$saveExperience, timedOut, timeoutId, timeout, _iterator, _step, resourceId, bodiedSyncBlockNodes, bodiedSyncBlockData, writeResults, _this$saveExperience2, _this$saveExperience3, attributionEnabled, _this$fireAnalyticsEv2, _this$flushCompletion, _t;
|
|
169
170
|
return _regenerator.default.wrap(function (_context) {
|
|
170
171
|
while (1) switch (_context.prev = _context.next) {
|
|
171
172
|
case 0:
|
|
@@ -284,11 +285,12 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
284
285
|
return _context.abrupt("return", true);
|
|
285
286
|
case 7:
|
|
286
287
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure();
|
|
288
|
+
attributionEnabled = (0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3');
|
|
287
289
|
writeResults.filter(function (result) {
|
|
288
290
|
return !result.resourceId || result.error;
|
|
289
291
|
}).forEach(function (result) {
|
|
290
292
|
var _this2$fireAnalyticsE2;
|
|
291
|
-
(_this2$fireAnalyticsE2 = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE2 === void 0 || _this2$fireAnalyticsE2.call(_this2, (0, _errorHandling.updateErrorPayload)(result.error || 'Failed to write data', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId)));
|
|
293
|
+
(_this2$fireAnalyticsE2 = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE2 === void 0 || _this2$fireAnalyticsE2.call(_this2, (0, _errorHandling.updateErrorPayload)(result.error || 'Failed to write data', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId), (0, _errorHandling.buildErrorAttribution)(attributionEnabled, result.error, result.statusCode)));
|
|
292
294
|
});
|
|
293
295
|
return _context.abrupt("return", false);
|
|
294
296
|
case 8:
|
|
@@ -300,8 +302,9 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
300
302
|
(0, _monitoring.logException)(_t, {
|
|
301
303
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
302
304
|
});
|
|
303
|
-
// Top-level flush failure is not tied to a single resourceId.
|
|
304
|
-
|
|
305
|
+
// Top-level flush failure is not tied to a single resourceId. There is no structured
|
|
306
|
+
// SyncBlockError/status here, so the attribution `reason` falls back to `unknown`.
|
|
307
|
+
(_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 || _this$fireAnalyticsEv2.call(this, (0, _errorHandling.updateErrorPayload)(_t.message, undefined, undefined, (0, _errorHandling.buildErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'))));
|
|
305
308
|
return _context.abrupt("return", false);
|
|
306
309
|
case 10:
|
|
307
310
|
_context.prev = 10;
|
|
@@ -476,7 +479,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
476
479
|
(_this4$createExperien2 = _this4.createExperience) === null || _this4$createExperien2 === void 0 || _this4$createExperien2.failure({
|
|
477
480
|
reason: result.error || 'Failed to create bodied sync block'
|
|
478
481
|
});
|
|
479
|
-
(_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, (0, _errorHandling.createErrorPayload)(result.error || 'Failed to create bodied sync block', resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
482
|
+
(_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, (0, _errorHandling.createErrorPayload)(result.error || 'Failed to create bodied sync block', resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), result.error, result.statusCode)));
|
|
480
483
|
}
|
|
481
484
|
}).catch(function (error) {
|
|
482
485
|
var _this4$createExperien3, _this4$fireAnalyticsE2;
|
|
@@ -508,7 +511,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
508
511
|
value: function () {
|
|
509
512
|
var _delete2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted, reason) {
|
|
510
513
|
var _this5 = this;
|
|
511
|
-
var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, _t2;
|
|
514
|
+
var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, attributionEnabled, attribution, _t2;
|
|
512
515
|
return _regenerator.default.wrap(function (_context2) {
|
|
513
516
|
while (1) switch (_context2.prev = _context2.next) {
|
|
514
517
|
case 0:
|
|
@@ -555,13 +558,14 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
555
558
|
_this5.setPendingDeletion(Ids, false);
|
|
556
559
|
};
|
|
557
560
|
(_this$deleteExperienc3 = this.deleteExperience) === null || _this$deleteExperienc3 === void 0 || _this$deleteExperienc3.failure();
|
|
561
|
+
attributionEnabled = (0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3');
|
|
558
562
|
results.forEach(function (result) {
|
|
559
563
|
if (result.success) {
|
|
560
564
|
var _this5$fireAnalyticsE2;
|
|
561
565
|
(_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, (0, _errorHandling.deleteSuccessPayload)(result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId)));
|
|
562
566
|
} else {
|
|
563
567
|
var _this5$fireAnalyticsE3;
|
|
564
|
-
(_this5$fireAnalyticsE3 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE3 === void 0 || _this5$fireAnalyticsE3.call(_this5, (0, _errorHandling.deleteErrorPayload)(result.error || 'Failed to delete synced block', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId)));
|
|
568
|
+
(_this5$fireAnalyticsE3 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE3 === void 0 || _this5$fireAnalyticsE3.call(_this5, (0, _errorHandling.deleteErrorPayload)(result.error || 'Failed to delete synced block', result.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(result.resourceId), (0, _errorHandling.buildErrorAttribution)(attributionEnabled, result.error, result.statusCode)));
|
|
565
569
|
}
|
|
566
570
|
});
|
|
567
571
|
}
|
|
@@ -570,10 +574,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
570
574
|
case 4:
|
|
571
575
|
_context2.prev = 4;
|
|
572
576
|
_t2 = _context2["catch"](0);
|
|
577
|
+
// Thrown (non-result) failure — no structured SyncBlockError/status is available,
|
|
578
|
+
// so the attribution `reason` falls back to `unknown` when the gate is on.
|
|
579
|
+
attribution = (0, _errorHandling.buildErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'));
|
|
573
580
|
syncBlockIds.forEach(function (Ids) {
|
|
574
581
|
var _this5$fireAnalyticsE4;
|
|
575
582
|
_this5.setPendingDeletion(Ids, false);
|
|
576
|
-
(_this5$fireAnalyticsE4 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE4 === void 0 || _this5$fireAnalyticsE4.call(_this5, (0, _errorHandling.deleteErrorPayload)(_t2.message, Ids.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(Ids.resourceId)));
|
|
583
|
+
(_this5$fireAnalyticsE4 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE4 === void 0 || _this5$fireAnalyticsE4.call(_this5, (0, _errorHandling.deleteErrorPayload)(_t2.message, Ids.resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(Ids.resourceId), attribution));
|
|
577
584
|
});
|
|
578
585
|
(0, _monitoring.logException)(_t2, {
|
|
579
586
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
@@ -4,9 +4,10 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = exports.getErrorPayload = exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayloadNew = exports.createSuccessPayload = exports.createErrorPayload = exports.cacheDeletionForcedPayload = void 0;
|
|
7
|
+
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = exports.getErrorPayload = exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayloadNew = exports.createSuccessPayload = exports.createErrorPayload = exports.classifyErrorReason = exports.cacheDeletionForcedPayload = exports.buildErrorAttribution = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
10
|
+
var _types = require("../common/types");
|
|
10
11
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
11
12
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
12
13
|
var stringifyError = exports.stringifyError = function stringifyError(error) {
|
|
@@ -17,24 +18,77 @@ var stringifyError = exports.stringifyError = function stringifyError(error) {
|
|
|
17
18
|
}
|
|
18
19
|
};
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The set of categorical failure reasons emitted on synced-block operational error
|
|
23
|
+
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
24
|
+
* failures down by cause (Block Service / HG / Relay) instead of relying on the
|
|
25
|
+
* free-text `error` blob. Mirrors the {@link SyncBlockError} enum values, plus an
|
|
26
|
+
* `unknown` fallback for unclassifiable errors.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
var SYNC_BLOCK_ERROR_REASONS = new Set(Object.values(_types.SyncBlockError));
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Maps a result `error` field (which may be a {@link SyncBlockError} enum value such as
|
|
33
|
+
* `'not_found'`, or an arbitrary stringified blob) to a stable categorical
|
|
34
|
+
* {@link SyncBlockErrorReason} for analytics grouping. Anything that is not a known
|
|
35
|
+
* `SyncBlockError` value collapses to `'unknown'` so the dashboard never has to group on
|
|
36
|
+
* free-text error blobs.
|
|
37
|
+
*/
|
|
38
|
+
var classifyErrorReason = exports.classifyErrorReason = function classifyErrorReason(error) {
|
|
39
|
+
if (error && SYNC_BLOCK_ERROR_REASONS.has(error)) {
|
|
40
|
+
return error;
|
|
41
|
+
}
|
|
42
|
+
return 'unknown';
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Extra, optional analytics attributes describing WHY an operational synced-block
|
|
47
|
+
* action failed. Spread conditionally so we never emit `undefined` keys (EDITOR-7796).
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Builds the {@link ErrorAttributionAttributes} for a failed synced-block operation from
|
|
52
|
+
* the raw result `error` field and optional backend `statusCode`. Returns `undefined`
|
|
53
|
+
* when the `platform_editor_blocks_patch_3` gate is OFF, so the new `reason`/`statusCode`
|
|
54
|
+
* attributes are only emitted once the gate is rolled out (EDITOR-7796).
|
|
55
|
+
*
|
|
56
|
+
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
57
|
+
* helper stays pure and trivially unit-testable for both gate states.
|
|
58
|
+
*/
|
|
59
|
+
var buildErrorAttribution = exports.buildErrorAttribution = function buildErrorAttribution(gateEnabled, error, statusCode) {
|
|
60
|
+
if (!gateEnabled) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return _objectSpread({
|
|
64
|
+
reason: classifyErrorReason(error)
|
|
65
|
+
}, statusCode !== undefined && {
|
|
66
|
+
statusCode: statusCode
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
20
70
|
// `sourceProduct` is threaded through every helper so analytics
|
|
21
71
|
// events can be attributed to the source product (`confluence-page` vs `jira-work-item`).
|
|
22
72
|
// All helpers accept it as an optional trailing argument; the spread-only-when-truthy
|
|
23
73
|
// pattern below ensures we never emit `sourceProduct: undefined` (which would dirty the
|
|
24
74
|
// event schema with empty keys).
|
|
25
75
|
|
|
26
|
-
var getErrorPayload = exports.getErrorPayload = function getErrorPayload(actionSubjectId, error, resourceId, sourceProduct) {
|
|
76
|
+
var getErrorPayload = exports.getErrorPayload = function getErrorPayload(actionSubjectId, error, resourceId, sourceProduct, attribution) {
|
|
27
77
|
return {
|
|
28
78
|
action: _analytics.ACTION.ERROR,
|
|
29
79
|
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
|
|
30
80
|
actionSubjectId: actionSubjectId,
|
|
31
81
|
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
32
|
-
attributes: _objectSpread(_objectSpread({
|
|
82
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
33
83
|
error: error
|
|
34
84
|
}, resourceId && {
|
|
35
85
|
resourceId: resourceId
|
|
36
86
|
}), sourceProduct && {
|
|
37
87
|
sourceProduct: sourceProduct
|
|
88
|
+
}), (attribution === null || attribution === void 0 ? void 0 : attribution.reason) && {
|
|
89
|
+
reason: attribution.reason
|
|
90
|
+
}), (attribution === null || attribution === void 0 ? void 0 : attribution.statusCode) !== undefined && {
|
|
91
|
+
statusCode: attribution.statusCode
|
|
38
92
|
})
|
|
39
93
|
};
|
|
40
94
|
};
|
|
@@ -44,17 +98,17 @@ var fetchErrorPayload = exports.fetchErrorPayload = function fetchErrorPayload(e
|
|
|
44
98
|
var getSourceInfoErrorPayload = exports.getSourceInfoErrorPayload = function getSourceInfoErrorPayload(error, resourceId, sourceProduct) {
|
|
45
99
|
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_GET_SOURCE_INFO, error, resourceId, sourceProduct);
|
|
46
100
|
};
|
|
47
|
-
var updateErrorPayload = exports.updateErrorPayload = function updateErrorPayload(error, resourceId, sourceProduct) {
|
|
48
|
-
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
101
|
+
var updateErrorPayload = exports.updateErrorPayload = function updateErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
102
|
+
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
49
103
|
};
|
|
50
|
-
var updateReferenceErrorPayload = exports.updateReferenceErrorPayload = function updateReferenceErrorPayload(error, resourceId, sourceProduct) {
|
|
51
|
-
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
104
|
+
var updateReferenceErrorPayload = exports.updateReferenceErrorPayload = function updateReferenceErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
105
|
+
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
52
106
|
};
|
|
53
|
-
var createErrorPayload = exports.createErrorPayload = function createErrorPayload(error, resourceId, sourceProduct) {
|
|
54
|
-
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct);
|
|
107
|
+
var createErrorPayload = exports.createErrorPayload = function createErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
108
|
+
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct, attribution);
|
|
55
109
|
};
|
|
56
|
-
var deleteErrorPayload = exports.deleteErrorPayload = function deleteErrorPayload(error, resourceId, sourceProduct) {
|
|
57
|
-
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct);
|
|
110
|
+
var deleteErrorPayload = exports.deleteErrorPayload = function deleteErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
111
|
+
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct, attribution);
|
|
58
112
|
};
|
|
59
113
|
var updateCacheErrorPayload = exports.updateCacheErrorPayload = function updateCacheErrorPayload(error, resourceId, sourceProduct) {
|
|
60
114
|
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE_CACHE, error, resourceId, sourceProduct);
|
|
@@ -387,7 +387,8 @@ export const writeDataBatch = async (cloudId, parentAri, parentId, product, data
|
|
|
387
387
|
if (error instanceof BlockError) {
|
|
388
388
|
return data.map(block => ({
|
|
389
389
|
error: mapBlockError(error),
|
|
390
|
-
resourceId: block.resourceId
|
|
390
|
+
resourceId: block.resourceId,
|
|
391
|
+
statusCode: error.status
|
|
391
392
|
}));
|
|
392
393
|
}
|
|
393
394
|
return data.map(block => ({
|
|
@@ -632,7 +633,8 @@ class BlockServiceADFWriteProvider {
|
|
|
632
633
|
if (error instanceof BlockError) {
|
|
633
634
|
return {
|
|
634
635
|
error: mapBlockError(error),
|
|
635
|
-
resourceId
|
|
636
|
+
resourceId,
|
|
637
|
+
statusCode: error.status
|
|
636
638
|
};
|
|
637
639
|
}
|
|
638
640
|
return {
|
|
@@ -675,7 +677,8 @@ class BlockServiceADFWriteProvider {
|
|
|
675
677
|
if (error instanceof BlockError) {
|
|
676
678
|
return {
|
|
677
679
|
error: mapBlockError(error),
|
|
678
|
-
resourceId
|
|
680
|
+
resourceId,
|
|
681
|
+
statusCode: error.status
|
|
679
682
|
};
|
|
680
683
|
}
|
|
681
684
|
return {
|
|
@@ -731,7 +734,8 @@ class BlockServiceADFWriteProvider {
|
|
|
731
734
|
return {
|
|
732
735
|
resourceId,
|
|
733
736
|
success: false,
|
|
734
|
-
error: mapBlockError(error)
|
|
737
|
+
error: mapBlockError(error),
|
|
738
|
+
statusCode: error.status
|
|
735
739
|
};
|
|
736
740
|
}
|
|
737
741
|
return {
|
|
@@ -772,7 +776,8 @@ class BlockServiceADFWriteProvider {
|
|
|
772
776
|
return {
|
|
773
777
|
resourceId,
|
|
774
778
|
success: false,
|
|
775
|
-
error: mapBlockError(createError)
|
|
779
|
+
error: mapBlockError(createError),
|
|
780
|
+
statusCode: createError.status
|
|
776
781
|
};
|
|
777
782
|
} else {
|
|
778
783
|
return {
|
|
@@ -796,7 +801,8 @@ class BlockServiceADFWriteProvider {
|
|
|
796
801
|
return {
|
|
797
802
|
resourceId,
|
|
798
803
|
success: false,
|
|
799
|
-
error: mapBlockError(error)
|
|
804
|
+
error: mapBlockError(error),
|
|
805
|
+
statusCode: error.status
|
|
800
806
|
};
|
|
801
807
|
}
|
|
802
808
|
return {
|
|
@@ -848,7 +854,8 @@ class BlockServiceADFWriteProvider {
|
|
|
848
854
|
if (error instanceof BlockError) {
|
|
849
855
|
return {
|
|
850
856
|
success: false,
|
|
851
|
-
error: mapBlockError(error)
|
|
857
|
+
error: mapBlockError(error),
|
|
858
|
+
statusCode: error.status
|
|
852
859
|
};
|
|
853
860
|
}
|
|
854
861
|
return {
|
|
@@ -923,7 +930,8 @@ class BlockServiceADFWriteProvider {
|
|
|
923
930
|
if (error instanceof BlockError) {
|
|
924
931
|
return data.map(block => ({
|
|
925
932
|
error: mapBlockError(error),
|
|
926
|
-
resourceId: block.resourceId
|
|
933
|
+
resourceId: block.resourceId,
|
|
934
|
+
statusCode: error.status
|
|
927
935
|
}));
|
|
928
936
|
}
|
|
929
937
|
return data.map(block => ({
|
|
@@ -3,7 +3,7 @@ import isEqual from 'lodash/isEqual';
|
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
5
|
import { SyncBlockError } from '../common/types';
|
|
6
|
-
import { cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
6
|
+
import { buildErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
7
7
|
import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
|
|
8
8
|
import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
|
|
9
9
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe, normalizeSyncBlockJSONContent } from '../utils/utils';
|
|
@@ -874,7 +874,7 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
874
874
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 ? void 0 : _this$saveExperience2.failure({
|
|
875
875
|
reason: updateResult.error || 'Failed to update reference synced blocks on the document'
|
|
876
876
|
});
|
|
877
|
-
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
|
|
877
|
+
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document', undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'), updateResult.error, updateResult.statusCode)));
|
|
878
878
|
}
|
|
879
879
|
} catch (error) {
|
|
880
880
|
var _this$saveExperience3, _this$fireAnalyticsEv13;
|
|
@@ -886,7 +886,9 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
886
886
|
reason: error.message
|
|
887
887
|
});
|
|
888
888
|
// No `resourceId` available in this catch — sourceProduct is intentionally omitted.
|
|
889
|
-
|
|
889
|
+
// No structured SyncBlockError/status here, so the attribution `reason` falls back
|
|
890
|
+
// to `unknown` when the gate is on.
|
|
891
|
+
(_this$fireAnalyticsEv13 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv13 === void 0 ? void 0 : _this$fireAnalyticsEv13.call(this, updateReferenceErrorPayload(error.message, undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'))));
|
|
890
892
|
} finally {
|
|
891
893
|
if (!success) {
|
|
892
894
|
// set isCacheDirty back to true for cases where it failed to update the reference synced blocks on the BE
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
3
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
4
|
import { SyncBlockError } from '../common/types';
|
|
4
|
-
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload } from '../utils/errorHandling';
|
|
5
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
5
6
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
6
7
|
import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
7
8
|
/** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
|
|
@@ -236,9 +237,10 @@ export class SourceSyncBlockStoreManager {
|
|
|
236
237
|
} else {
|
|
237
238
|
var _this$saveExperience3;
|
|
238
239
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 ? void 0 : _this$saveExperience3.failure();
|
|
240
|
+
const attributionEnabled = fg('platform_editor_blocks_patch_3');
|
|
239
241
|
writeResults.filter(result => !result.resourceId || result.error).forEach(result => {
|
|
240
242
|
var _this$fireAnalyticsEv3;
|
|
241
|
-
(_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 ? void 0 : _this$fireAnalyticsEv3.call(this, updateErrorPayload(result.error || 'Failed to write data', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
243
|
+
(_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 ? void 0 : _this$fireAnalyticsEv3.call(this, updateErrorPayload(result.error || 'Failed to write data', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
|
|
242
244
|
});
|
|
243
245
|
return false;
|
|
244
246
|
}
|
|
@@ -247,8 +249,9 @@ export class SourceSyncBlockStoreManager {
|
|
|
247
249
|
logException(error, {
|
|
248
250
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
249
251
|
});
|
|
250
|
-
// Top-level flush failure is not tied to a single resourceId.
|
|
251
|
-
|
|
252
|
+
// Top-level flush failure is not tied to a single resourceId. There is no structured
|
|
253
|
+
// SyncBlockError/status here, so the attribution `reason` falls back to `unknown`.
|
|
254
|
+
(_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 ? void 0 : _this$fireAnalyticsEv4.call(this, updateErrorPayload(error.message, undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'))));
|
|
252
255
|
return false;
|
|
253
256
|
} finally {
|
|
254
257
|
var _this$flushCompletion;
|
|
@@ -396,7 +399,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
396
399
|
(_this$createExperienc3 = this.createExperience) === null || _this$createExperienc3 === void 0 ? void 0 : _this$createExperienc3.failure({
|
|
397
400
|
reason: result.error || 'Failed to create bodied sync block'
|
|
398
401
|
});
|
|
399
|
-
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
402
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 ? void 0 : _this$fireAnalyticsEv8.call(this, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(fg('platform_editor_blocks_patch_3'), result.error, result.statusCode)));
|
|
400
403
|
}
|
|
401
404
|
}).catch(error => {
|
|
402
405
|
var _this$createExperienc4, _this$fireAnalyticsEv9;
|
|
@@ -456,23 +459,27 @@ export class SourceSyncBlockStoreManager {
|
|
|
456
459
|
this.setPendingDeletion(Ids, false);
|
|
457
460
|
};
|
|
458
461
|
(_this$deleteExperienc3 = this.deleteExperience) === null || _this$deleteExperienc3 === void 0 ? void 0 : _this$deleteExperienc3.failure();
|
|
462
|
+
const attributionEnabled = fg('platform_editor_blocks_patch_3');
|
|
459
463
|
results.forEach(result => {
|
|
460
464
|
if (result.success) {
|
|
461
465
|
var _this$fireAnalyticsEv10;
|
|
462
466
|
(_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, deleteSuccessPayload(result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
463
467
|
} else {
|
|
464
468
|
var _this$fireAnalyticsEv11;
|
|
465
|
-
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
469
|
+
(_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
|
|
466
470
|
}
|
|
467
471
|
});
|
|
468
472
|
}
|
|
469
473
|
syncBlockIds.forEach(callback);
|
|
470
474
|
return isDeleteSuccessful;
|
|
471
475
|
} catch (error) {
|
|
476
|
+
// Thrown (non-result) failure — no structured SyncBlockError/status is available,
|
|
477
|
+
// so the attribution `reason` falls back to `unknown` when the gate is on.
|
|
478
|
+
const attribution = buildErrorAttribution(fg('platform_editor_blocks_patch_3'));
|
|
472
479
|
syncBlockIds.forEach(Ids => {
|
|
473
480
|
var _this$fireAnalyticsEv12;
|
|
474
481
|
this.setPendingDeletion(Ids, false);
|
|
475
|
-
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, deleteErrorPayload(error.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId)));
|
|
482
|
+
(_this$fireAnalyticsEv12 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv12 === void 0 ? void 0 : _this$fireAnalyticsEv12.call(this, deleteErrorPayload(error.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
|
|
476
483
|
});
|
|
477
484
|
logException(error, {
|
|
478
485
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { SyncBlockError } from '../common/types';
|
|
2
3
|
export const stringifyError = error => {
|
|
3
4
|
try {
|
|
4
5
|
return JSON.stringify(error);
|
|
@@ -7,13 +8,63 @@ export const stringifyError = error => {
|
|
|
7
8
|
}
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* The set of categorical failure reasons emitted on synced-block operational error
|
|
13
|
+
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
14
|
+
* failures down by cause (Block Service / HG / Relay) instead of relying on the
|
|
15
|
+
* free-text `error` blob. Mirrors the {@link SyncBlockError} enum values, plus an
|
|
16
|
+
* `unknown` fallback for unclassifiable errors.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const SYNC_BLOCK_ERROR_REASONS = new Set(Object.values(SyncBlockError));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Maps a result `error` field (which may be a {@link SyncBlockError} enum value such as
|
|
23
|
+
* `'not_found'`, or an arbitrary stringified blob) to a stable categorical
|
|
24
|
+
* {@link SyncBlockErrorReason} for analytics grouping. Anything that is not a known
|
|
25
|
+
* `SyncBlockError` value collapses to `'unknown'` so the dashboard never has to group on
|
|
26
|
+
* free-text error blobs.
|
|
27
|
+
*/
|
|
28
|
+
export const classifyErrorReason = error => {
|
|
29
|
+
if (error && SYNC_BLOCK_ERROR_REASONS.has(error)) {
|
|
30
|
+
return error;
|
|
31
|
+
}
|
|
32
|
+
return 'unknown';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Extra, optional analytics attributes describing WHY an operational synced-block
|
|
37
|
+
* action failed. Spread conditionally so we never emit `undefined` keys (EDITOR-7796).
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds the {@link ErrorAttributionAttributes} for a failed synced-block operation from
|
|
42
|
+
* the raw result `error` field and optional backend `statusCode`. Returns `undefined`
|
|
43
|
+
* when the `platform_editor_blocks_patch_3` gate is OFF, so the new `reason`/`statusCode`
|
|
44
|
+
* attributes are only emitted once the gate is rolled out (EDITOR-7796).
|
|
45
|
+
*
|
|
46
|
+
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
47
|
+
* helper stays pure and trivially unit-testable for both gate states.
|
|
48
|
+
*/
|
|
49
|
+
export const buildErrorAttribution = (gateEnabled, error, statusCode) => {
|
|
50
|
+
if (!gateEnabled) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
reason: classifyErrorReason(error),
|
|
55
|
+
...(statusCode !== undefined && {
|
|
56
|
+
statusCode
|
|
57
|
+
})
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
10
61
|
// `sourceProduct` is threaded through every helper so analytics
|
|
11
62
|
// events can be attributed to the source product (`confluence-page` vs `jira-work-item`).
|
|
12
63
|
// All helpers accept it as an optional trailing argument; the spread-only-when-truthy
|
|
13
64
|
// pattern below ensures we never emit `sourceProduct: undefined` (which would dirty the
|
|
14
65
|
// event schema with empty keys).
|
|
15
66
|
|
|
16
|
-
export const getErrorPayload = (actionSubjectId, error, resourceId, sourceProduct) => ({
|
|
67
|
+
export const getErrorPayload = (actionSubjectId, error, resourceId, sourceProduct, attribution) => ({
|
|
17
68
|
action: ACTION.ERROR,
|
|
18
69
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
19
70
|
actionSubjectId,
|
|
@@ -25,15 +76,21 @@ export const getErrorPayload = (actionSubjectId, error, resourceId, sourceProduc
|
|
|
25
76
|
}),
|
|
26
77
|
...(sourceProduct && {
|
|
27
78
|
sourceProduct
|
|
79
|
+
}),
|
|
80
|
+
...((attribution === null || attribution === void 0 ? void 0 : attribution.reason) && {
|
|
81
|
+
reason: attribution.reason
|
|
82
|
+
}),
|
|
83
|
+
...((attribution === null || attribution === void 0 ? void 0 : attribution.statusCode) !== undefined && {
|
|
84
|
+
statusCode: attribution.statusCode
|
|
28
85
|
})
|
|
29
86
|
}
|
|
30
87
|
});
|
|
31
88
|
export const fetchErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_FETCH, error, resourceId, sourceProduct);
|
|
32
89
|
export const getSourceInfoErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_GET_SOURCE_INFO, error, resourceId, sourceProduct);
|
|
33
|
-
export const updateErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
34
|
-
export const updateReferenceErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
35
|
-
export const createErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct);
|
|
36
|
-
export const deleteErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct);
|
|
90
|
+
export const updateErrorPayload = (error, resourceId, sourceProduct, attribution) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
91
|
+
export const updateReferenceErrorPayload = (error, resourceId, sourceProduct, attribution) => getErrorPayload(ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
92
|
+
export const createErrorPayload = (error, resourceId, sourceProduct, attribution) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct, attribution);
|
|
93
|
+
export const deleteErrorPayload = (error, resourceId, sourceProduct, attribution) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct, attribution);
|
|
37
94
|
export const updateCacheErrorPayload = (error, resourceId, sourceProduct) => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE_CACHE, error, resourceId, sourceProduct);
|
|
38
95
|
/**
|
|
39
96
|
* Payload for `SYNCED_BLOCK_SOURCE_INFO_ORPHANED`. Fired when source-info
|
|
@@ -567,7 +567,8 @@ export var writeDataBatch = /*#__PURE__*/function () {
|
|
|
567
567
|
return _context4.abrupt("return", data.map(function (block) {
|
|
568
568
|
return {
|
|
569
569
|
error: mapBlockError(_t6),
|
|
570
|
-
resourceId: block.resourceId
|
|
570
|
+
resourceId: block.resourceId,
|
|
571
|
+
statusCode: _t6.status
|
|
571
572
|
};
|
|
572
573
|
}));
|
|
573
574
|
case 11:
|
|
@@ -913,7 +914,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
913
914
|
}
|
|
914
915
|
return _context8.abrupt("return", {
|
|
915
916
|
error: mapBlockError(_t0),
|
|
916
|
-
resourceId: resourceId
|
|
917
|
+
resourceId: resourceId,
|
|
918
|
+
statusCode: _t0.status
|
|
917
919
|
});
|
|
918
920
|
case 8:
|
|
919
921
|
return _context8.abrupt("return", {
|
|
@@ -993,7 +995,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
993
995
|
}
|
|
994
996
|
return _context9.abrupt("return", {
|
|
995
997
|
error: mapBlockError(_t10),
|
|
996
|
-
resourceId: resourceId
|
|
998
|
+
resourceId: resourceId,
|
|
999
|
+
statusCode: _t10.status
|
|
997
1000
|
});
|
|
998
1001
|
case 8:
|
|
999
1002
|
return _context9.abrupt("return", {
|
|
@@ -1077,7 +1080,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1077
1080
|
return _context0.abrupt("return", {
|
|
1078
1081
|
resourceId: resourceId,
|
|
1079
1082
|
success: false,
|
|
1080
|
-
error: mapBlockError(_t11)
|
|
1083
|
+
error: mapBlockError(_t11),
|
|
1084
|
+
statusCode: _t11.status
|
|
1081
1085
|
});
|
|
1082
1086
|
case 7:
|
|
1083
1087
|
return _context0.abrupt("return", {
|
|
@@ -1152,7 +1156,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1152
1156
|
return _context1.abrupt("return", {
|
|
1153
1157
|
resourceId: resourceId,
|
|
1154
1158
|
success: false,
|
|
1155
|
-
error: mapBlockError(_t13)
|
|
1159
|
+
error: mapBlockError(_t13),
|
|
1160
|
+
statusCode: _t13.status
|
|
1156
1161
|
});
|
|
1157
1162
|
case 9:
|
|
1158
1163
|
return _context1.abrupt("return", {
|
|
@@ -1182,7 +1187,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1182
1187
|
return _context1.abrupt("return", {
|
|
1183
1188
|
resourceId: resourceId,
|
|
1184
1189
|
success: false,
|
|
1185
|
-
error: mapBlockError(_t14)
|
|
1190
|
+
error: mapBlockError(_t14),
|
|
1191
|
+
statusCode: _t14.status
|
|
1186
1192
|
});
|
|
1187
1193
|
case 13:
|
|
1188
1194
|
return _context1.abrupt("return", {
|
|
@@ -1267,7 +1273,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1267
1273
|
}
|
|
1268
1274
|
return _context10.abrupt("return", {
|
|
1269
1275
|
success: false,
|
|
1270
|
-
error: mapBlockError(_t15)
|
|
1276
|
+
error: mapBlockError(_t15),
|
|
1277
|
+
statusCode: _t15.status
|
|
1271
1278
|
});
|
|
1272
1279
|
case 4:
|
|
1273
1280
|
return _context10.abrupt("return", {
|
|
@@ -1434,7 +1441,8 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
|
|
|
1434
1441
|
return _context12.abrupt("return", data.map(function (block) {
|
|
1435
1442
|
return {
|
|
1436
1443
|
error: mapBlockError(_t18),
|
|
1437
|
-
resourceId: block.resourceId
|
|
1444
|
+
resourceId: block.resourceId,
|
|
1445
|
+
statusCode: _t18.status
|
|
1438
1446
|
};
|
|
1439
1447
|
}));
|
|
1440
1448
|
case 15:
|
|
@@ -13,7 +13,7 @@ import isEqual from 'lodash/isEqual';
|
|
|
13
13
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
14
14
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
15
15
|
import { SyncBlockError } from '../common/types';
|
|
16
|
-
import { cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
16
|
+
import { buildErrorAttribution, cacheDeletionForcedPayload, fetchErrorPayload, fetchSuccessPayload, getSourceInfoErrorPayload, sourceInfoOrphanedPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
|
|
17
17
|
import { getFetchExperience, getFetchSourceInfoExperience, getSaveReferenceExperience } from '../utils/experienceTracking';
|
|
18
18
|
import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
|
|
19
19
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe, normalizeSyncBlockJSONContent } from '../utils/utils';
|
|
@@ -1077,7 +1077,7 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1077
1077
|
(_this$saveExperience2 = this.saveExperience) === null || _this$saveExperience2 === void 0 || _this$saveExperience2.failure({
|
|
1078
1078
|
reason: updateResult.error || 'Failed to update reference synced blocks on the document'
|
|
1079
1079
|
});
|
|
1080
|
-
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
|
|
1080
|
+
(_this$fireAnalyticsEv8 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv8 === void 0 || _this$fireAnalyticsEv8.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document', undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'), updateResult.error, updateResult.statusCode)));
|
|
1081
1081
|
}
|
|
1082
1082
|
_context4.next = 17;
|
|
1083
1083
|
break;
|
|
@@ -1092,7 +1092,9 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
1092
1092
|
reason: _t3.message
|
|
1093
1093
|
});
|
|
1094
1094
|
// No `resourceId` available in this catch — sourceProduct is intentionally omitted.
|
|
1095
|
-
|
|
1095
|
+
// No structured SyncBlockError/status here, so the attribution `reason` falls back
|
|
1096
|
+
// to `unknown` when the gate is on.
|
|
1097
|
+
(_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, updateReferenceErrorPayload(_t3.message, undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'))));
|
|
1096
1098
|
case 17:
|
|
1097
1099
|
_context4.prev = 17;
|
|
1098
1100
|
if (!success) {
|
|
@@ -10,8 +10,9 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
10
10
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
11
11
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
12
12
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
13
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
13
14
|
import { SyncBlockError } from '../common/types';
|
|
14
|
-
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload } from '../utils/errorHandling';
|
|
15
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
15
16
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
16
17
|
import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
17
18
|
/** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
|
|
@@ -158,7 +159,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
158
159
|
value: (function () {
|
|
159
160
|
var _flush = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
160
161
|
var _this2 = this;
|
|
161
|
-
var _this$saveExperience, timedOut, timeoutId, timeout, _iterator, _step, resourceId, bodiedSyncBlockNodes, bodiedSyncBlockData, writeResults, _this$saveExperience2, _this$saveExperience3, _this$fireAnalyticsEv2, _this$flushCompletion, _t;
|
|
162
|
+
var _this$saveExperience, timedOut, timeoutId, timeout, _iterator, _step, resourceId, bodiedSyncBlockNodes, bodiedSyncBlockData, writeResults, _this$saveExperience2, _this$saveExperience3, attributionEnabled, _this$fireAnalyticsEv2, _this$flushCompletion, _t;
|
|
162
163
|
return _regeneratorRuntime.wrap(function (_context) {
|
|
163
164
|
while (1) switch (_context.prev = _context.next) {
|
|
164
165
|
case 0:
|
|
@@ -277,11 +278,12 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
277
278
|
return _context.abrupt("return", true);
|
|
278
279
|
case 7:
|
|
279
280
|
(_this$saveExperience3 = this.saveExperience) === null || _this$saveExperience3 === void 0 || _this$saveExperience3.failure();
|
|
281
|
+
attributionEnabled = fg('platform_editor_blocks_patch_3');
|
|
280
282
|
writeResults.filter(function (result) {
|
|
281
283
|
return !result.resourceId || result.error;
|
|
282
284
|
}).forEach(function (result) {
|
|
283
285
|
var _this2$fireAnalyticsE2;
|
|
284
|
-
(_this2$fireAnalyticsE2 = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE2 === void 0 || _this2$fireAnalyticsE2.call(_this2, updateErrorPayload(result.error || 'Failed to write data', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
286
|
+
(_this2$fireAnalyticsE2 = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE2 === void 0 || _this2$fireAnalyticsE2.call(_this2, updateErrorPayload(result.error || 'Failed to write data', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
|
|
285
287
|
});
|
|
286
288
|
return _context.abrupt("return", false);
|
|
287
289
|
case 8:
|
|
@@ -293,8 +295,9 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
293
295
|
logException(_t, {
|
|
294
296
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
295
297
|
});
|
|
296
|
-
// Top-level flush failure is not tied to a single resourceId.
|
|
297
|
-
|
|
298
|
+
// Top-level flush failure is not tied to a single resourceId. There is no structured
|
|
299
|
+
// SyncBlockError/status here, so the attribution `reason` falls back to `unknown`.
|
|
300
|
+
(_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 || _this$fireAnalyticsEv2.call(this, updateErrorPayload(_t.message, undefined, undefined, buildErrorAttribution(fg('platform_editor_blocks_patch_3'))));
|
|
298
301
|
return _context.abrupt("return", false);
|
|
299
302
|
case 10:
|
|
300
303
|
_context.prev = 10;
|
|
@@ -469,7 +472,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
469
472
|
(_this4$createExperien2 = _this4.createExperience) === null || _this4$createExperien2 === void 0 || _this4$createExperien2.failure({
|
|
470
473
|
reason: result.error || 'Failed to create bodied sync block'
|
|
471
474
|
});
|
|
472
|
-
(_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId)));
|
|
475
|
+
(_this4$fireAnalyticsE = _this4.fireAnalyticsEvent) === null || _this4$fireAnalyticsE === void 0 || _this4$fireAnalyticsE.call(_this4, createErrorPayload(result.error || 'Failed to create bodied sync block', resourceId, getSourceProductFromResourceIdSafe(resourceId), buildErrorAttribution(fg('platform_editor_blocks_patch_3'), result.error, result.statusCode)));
|
|
473
476
|
}
|
|
474
477
|
}).catch(function (error) {
|
|
475
478
|
var _this4$createExperien3, _this4$fireAnalyticsE2;
|
|
@@ -501,7 +504,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
501
504
|
value: function () {
|
|
502
505
|
var _delete2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(syncBlockIds, onDelete, onDeleteCompleted, reason) {
|
|
503
506
|
var _this5 = this;
|
|
504
|
-
var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, _t2;
|
|
507
|
+
var _this$deleteExperienc, results, callback, isDeleteSuccessful, _this$deleteExperienc2, _this$deleteExperienc3, attributionEnabled, attribution, _t2;
|
|
505
508
|
return _regeneratorRuntime.wrap(function (_context2) {
|
|
506
509
|
while (1) switch (_context2.prev = _context2.next) {
|
|
507
510
|
case 0:
|
|
@@ -548,13 +551,14 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
548
551
|
_this5.setPendingDeletion(Ids, false);
|
|
549
552
|
};
|
|
550
553
|
(_this$deleteExperienc3 = this.deleteExperience) === null || _this$deleteExperienc3 === void 0 || _this$deleteExperienc3.failure();
|
|
554
|
+
attributionEnabled = fg('platform_editor_blocks_patch_3');
|
|
551
555
|
results.forEach(function (result) {
|
|
552
556
|
if (result.success) {
|
|
553
557
|
var _this5$fireAnalyticsE2;
|
|
554
558
|
(_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, deleteSuccessPayload(result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
555
559
|
} else {
|
|
556
560
|
var _this5$fireAnalyticsE3;
|
|
557
|
-
(_this5$fireAnalyticsE3 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE3 === void 0 || _this5$fireAnalyticsE3.call(_this5, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId)));
|
|
561
|
+
(_this5$fireAnalyticsE3 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE3 === void 0 || _this5$fireAnalyticsE3.call(_this5, deleteErrorPayload(result.error || 'Failed to delete synced block', result.resourceId, getSourceProductFromResourceIdSafe(result.resourceId), buildErrorAttribution(attributionEnabled, result.error, result.statusCode)));
|
|
558
562
|
}
|
|
559
563
|
});
|
|
560
564
|
}
|
|
@@ -563,10 +567,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
563
567
|
case 4:
|
|
564
568
|
_context2.prev = 4;
|
|
565
569
|
_t2 = _context2["catch"](0);
|
|
570
|
+
// Thrown (non-result) failure — no structured SyncBlockError/status is available,
|
|
571
|
+
// so the attribution `reason` falls back to `unknown` when the gate is on.
|
|
572
|
+
attribution = buildErrorAttribution(fg('platform_editor_blocks_patch_3'));
|
|
566
573
|
syncBlockIds.forEach(function (Ids) {
|
|
567
574
|
var _this5$fireAnalyticsE4;
|
|
568
575
|
_this5.setPendingDeletion(Ids, false);
|
|
569
|
-
(_this5$fireAnalyticsE4 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE4 === void 0 || _this5$fireAnalyticsE4.call(_this5, deleteErrorPayload(_t2.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId)));
|
|
576
|
+
(_this5$fireAnalyticsE4 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE4 === void 0 || _this5$fireAnalyticsE4.call(_this5, deleteErrorPayload(_t2.message, Ids.resourceId, getSourceProductFromResourceIdSafe(Ids.resourceId), attribution));
|
|
570
577
|
});
|
|
571
578
|
logException(_t2, {
|
|
572
579
|
location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
|
|
@@ -2,6 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
3
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
4
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
5
|
+
import { SyncBlockError } from '../common/types';
|
|
5
6
|
export var stringifyError = function stringifyError(error) {
|
|
6
7
|
try {
|
|
7
8
|
return JSON.stringify(error);
|
|
@@ -10,24 +11,77 @@ export var stringifyError = function stringifyError(error) {
|
|
|
10
11
|
}
|
|
11
12
|
};
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The set of categorical failure reasons emitted on synced-block operational error
|
|
16
|
+
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
17
|
+
* failures down by cause (Block Service / HG / Relay) instead of relying on the
|
|
18
|
+
* free-text `error` blob. Mirrors the {@link SyncBlockError} enum values, plus an
|
|
19
|
+
* `unknown` fallback for unclassifiable errors.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
var SYNC_BLOCK_ERROR_REASONS = new Set(Object.values(SyncBlockError));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Maps a result `error` field (which may be a {@link SyncBlockError} enum value such as
|
|
26
|
+
* `'not_found'`, or an arbitrary stringified blob) to a stable categorical
|
|
27
|
+
* {@link SyncBlockErrorReason} for analytics grouping. Anything that is not a known
|
|
28
|
+
* `SyncBlockError` value collapses to `'unknown'` so the dashboard never has to group on
|
|
29
|
+
* free-text error blobs.
|
|
30
|
+
*/
|
|
31
|
+
export var classifyErrorReason = function classifyErrorReason(error) {
|
|
32
|
+
if (error && SYNC_BLOCK_ERROR_REASONS.has(error)) {
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
35
|
+
return 'unknown';
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extra, optional analytics attributes describing WHY an operational synced-block
|
|
40
|
+
* action failed. Spread conditionally so we never emit `undefined` keys (EDITOR-7796).
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Builds the {@link ErrorAttributionAttributes} for a failed synced-block operation from
|
|
45
|
+
* the raw result `error` field and optional backend `statusCode`. Returns `undefined`
|
|
46
|
+
* when the `platform_editor_blocks_patch_3` gate is OFF, so the new `reason`/`statusCode`
|
|
47
|
+
* attributes are only emitted once the gate is rolled out (EDITOR-7796).
|
|
48
|
+
*
|
|
49
|
+
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
50
|
+
* helper stays pure and trivially unit-testable for both gate states.
|
|
51
|
+
*/
|
|
52
|
+
export var buildErrorAttribution = function buildErrorAttribution(gateEnabled, error, statusCode) {
|
|
53
|
+
if (!gateEnabled) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return _objectSpread({
|
|
57
|
+
reason: classifyErrorReason(error)
|
|
58
|
+
}, statusCode !== undefined && {
|
|
59
|
+
statusCode: statusCode
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
13
63
|
// `sourceProduct` is threaded through every helper so analytics
|
|
14
64
|
// events can be attributed to the source product (`confluence-page` vs `jira-work-item`).
|
|
15
65
|
// All helpers accept it as an optional trailing argument; the spread-only-when-truthy
|
|
16
66
|
// pattern below ensures we never emit `sourceProduct: undefined` (which would dirty the
|
|
17
67
|
// event schema with empty keys).
|
|
18
68
|
|
|
19
|
-
export var getErrorPayload = function getErrorPayload(actionSubjectId, error, resourceId, sourceProduct) {
|
|
69
|
+
export var getErrorPayload = function getErrorPayload(actionSubjectId, error, resourceId, sourceProduct, attribution) {
|
|
20
70
|
return {
|
|
21
71
|
action: ACTION.ERROR,
|
|
22
72
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
23
73
|
actionSubjectId: actionSubjectId,
|
|
24
74
|
eventType: EVENT_TYPE.OPERATIONAL,
|
|
25
|
-
attributes: _objectSpread(_objectSpread({
|
|
75
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
26
76
|
error: error
|
|
27
77
|
}, resourceId && {
|
|
28
78
|
resourceId: resourceId
|
|
29
79
|
}), sourceProduct && {
|
|
30
80
|
sourceProduct: sourceProduct
|
|
81
|
+
}), (attribution === null || attribution === void 0 ? void 0 : attribution.reason) && {
|
|
82
|
+
reason: attribution.reason
|
|
83
|
+
}), (attribution === null || attribution === void 0 ? void 0 : attribution.statusCode) !== undefined && {
|
|
84
|
+
statusCode: attribution.statusCode
|
|
31
85
|
})
|
|
32
86
|
};
|
|
33
87
|
};
|
|
@@ -37,17 +91,17 @@ export var fetchErrorPayload = function fetchErrorPayload(error, resourceId, sou
|
|
|
37
91
|
export var getSourceInfoErrorPayload = function getSourceInfoErrorPayload(error, resourceId, sourceProduct) {
|
|
38
92
|
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_GET_SOURCE_INFO, error, resourceId, sourceProduct);
|
|
39
93
|
};
|
|
40
|
-
export var updateErrorPayload = function updateErrorPayload(error, resourceId, sourceProduct) {
|
|
41
|
-
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
94
|
+
export var updateErrorPayload = function updateErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
95
|
+
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
42
96
|
};
|
|
43
|
-
export var updateReferenceErrorPayload = function updateReferenceErrorPayload(error, resourceId, sourceProduct) {
|
|
44
|
-
return getErrorPayload(ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct);
|
|
97
|
+
export var updateReferenceErrorPayload = function updateReferenceErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
98
|
+
return getErrorPayload(ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error, resourceId, sourceProduct, attribution);
|
|
45
99
|
};
|
|
46
|
-
export var createErrorPayload = function createErrorPayload(error, resourceId, sourceProduct) {
|
|
47
|
-
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct);
|
|
100
|
+
export var createErrorPayload = function createErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
101
|
+
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error, resourceId, sourceProduct, attribution);
|
|
48
102
|
};
|
|
49
|
-
export var deleteErrorPayload = function deleteErrorPayload(error, resourceId, sourceProduct) {
|
|
50
|
-
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct);
|
|
103
|
+
export var deleteErrorPayload = function deleteErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
104
|
+
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error, resourceId, sourceProduct, attribution);
|
|
51
105
|
};
|
|
52
106
|
export var updateCacheErrorPayload = function updateCacheErrorPayload(error, resourceId, sourceProduct) {
|
|
53
107
|
return getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE_CACHE, error, resourceId, sourceProduct);
|
|
@@ -25,6 +25,12 @@ export type SyncBlockInstance = {
|
|
|
25
25
|
export type DeleteSyncBlockResult = {
|
|
26
26
|
error?: string;
|
|
27
27
|
resourceId: ResourceId;
|
|
28
|
+
/**
|
|
29
|
+
* HTTP status code from the backend (Block Service) when the failure originated
|
|
30
|
+
* from a `BlockError`. Surfaced so failure analytics can break down delete/update
|
|
31
|
+
* failures by status code (EDITOR-7796). Undefined for non-HTTP failures.
|
|
32
|
+
*/
|
|
33
|
+
statusCode?: number;
|
|
28
34
|
success: boolean;
|
|
29
35
|
};
|
|
30
36
|
/**
|
|
@@ -66,6 +72,12 @@ export type WriteSyncBlockResult = {
|
|
|
66
72
|
error?: string;
|
|
67
73
|
resourceId?: ResourceId;
|
|
68
74
|
status?: SyncBlockStatus;
|
|
75
|
+
/**
|
|
76
|
+
* HTTP status code from the backend (Block Service) when the failure originated
|
|
77
|
+
* from a `BlockError`. Surfaced so failure analytics can break down update
|
|
78
|
+
* failures by status code (EDITOR-7796). Undefined for non-HTTP failures.
|
|
79
|
+
*/
|
|
80
|
+
statusCode?: number;
|
|
69
81
|
};
|
|
70
82
|
export type SourceInfoFetchData = {
|
|
71
83
|
pageARI: string;
|
|
@@ -73,6 +85,12 @@ export type SourceInfoFetchData = {
|
|
|
73
85
|
};
|
|
74
86
|
export type UpdateReferenceSyncBlockResult = {
|
|
75
87
|
error?: string;
|
|
88
|
+
/**
|
|
89
|
+
* HTTP status code from the backend when the failure originated from a `BlockError`.
|
|
90
|
+
* Surfaced so reference-update failure analytics can break down failures by status
|
|
91
|
+
* code (EDITOR-7796). Undefined for non-HTTP failures.
|
|
92
|
+
*/
|
|
93
|
+
statusCode?: number;
|
|
76
94
|
success: boolean;
|
|
77
95
|
};
|
|
78
96
|
export type BlockNodeIdentifiers = {
|
|
@@ -1,17 +1,56 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import type { RendererSyncBlockEventPayload, OperationalAEP, SyncBlockEventPayload } from '@atlaskit/editor-common/analytics';
|
|
3
|
+
import { SyncBlockError } from '../common/types';
|
|
3
4
|
export declare const stringifyError: (error: unknown) => string | undefined;
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The set of categorical failure reasons emitted on synced-block operational error
|
|
7
|
+
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
8
|
+
* failures down by cause (Block Service / HG / Relay) instead of relying on the
|
|
9
|
+
* free-text `error` blob. Mirrors the {@link SyncBlockError} enum values, plus an
|
|
10
|
+
* `unknown` fallback for unclassifiable errors.
|
|
11
|
+
*/
|
|
12
|
+
export type SyncBlockErrorReason = `${SyncBlockError}` | 'unknown';
|
|
13
|
+
/**
|
|
14
|
+
* Maps a result `error` field (which may be a {@link SyncBlockError} enum value such as
|
|
15
|
+
* `'not_found'`, or an arbitrary stringified blob) to a stable categorical
|
|
16
|
+
* {@link SyncBlockErrorReason} for analytics grouping. Anything that is not a known
|
|
17
|
+
* `SyncBlockError` value collapses to `'unknown'` so the dashboard never has to group on
|
|
18
|
+
* free-text error blobs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const classifyErrorReason: (error?: string) => SyncBlockErrorReason;
|
|
21
|
+
/**
|
|
22
|
+
* Extra, optional analytics attributes describing WHY an operational synced-block
|
|
23
|
+
* action failed. Spread conditionally so we never emit `undefined` keys (EDITOR-7796).
|
|
24
|
+
*/
|
|
25
|
+
export type ErrorAttributionAttributes = {
|
|
26
|
+
/** Categorical failure cause for dashboard grouping. */
|
|
27
|
+
reason?: SyncBlockErrorReason;
|
|
28
|
+
/** Backend HTTP status code when the failure came from a `BlockError`. */
|
|
29
|
+
statusCode?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Builds the {@link ErrorAttributionAttributes} for a failed synced-block operation from
|
|
33
|
+
* the raw result `error` field and optional backend `statusCode`. Returns `undefined`
|
|
34
|
+
* when the `platform_editor_blocks_patch_3` gate is OFF, so the new `reason`/`statusCode`
|
|
35
|
+
* attributes are only emitted once the gate is rolled out (EDITOR-7796).
|
|
36
|
+
*
|
|
37
|
+
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
38
|
+
* helper stays pure and trivially unit-testable for both gate states.
|
|
39
|
+
*/
|
|
40
|
+
export declare const buildErrorAttribution: (gateEnabled: boolean, error?: string, statusCode?: number) => ErrorAttributionAttributes | undefined;
|
|
41
|
+
export declare const getErrorPayload: <T extends ACTION_SUBJECT_ID>(actionSubjectId: T, error: string, resourceId?: string, sourceProduct?: string, attribution?: ErrorAttributionAttributes) => OperationalAEP<ACTION.ERROR, ACTION_SUBJECT.SYNCED_BLOCK, T, {
|
|
5
42
|
error: string;
|
|
6
43
|
resourceId?: string;
|
|
7
44
|
sourceProduct?: string;
|
|
45
|
+
reason?: SyncBlockErrorReason;
|
|
46
|
+
statusCode?: number;
|
|
8
47
|
}>;
|
|
9
48
|
export declare const fetchErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => RendererSyncBlockEventPayload;
|
|
10
49
|
export declare const getSourceInfoErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => RendererSyncBlockEventPayload;
|
|
11
|
-
export declare const updateErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
12
|
-
export declare const updateReferenceErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => RendererSyncBlockEventPayload;
|
|
13
|
-
export declare const createErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
14
|
-
export declare const deleteErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
50
|
+
export declare const updateErrorPayload: (error: string, resourceId?: string, sourceProduct?: string, attribution?: ErrorAttributionAttributes) => SyncBlockEventPayload;
|
|
51
|
+
export declare const updateReferenceErrorPayload: (error: string, resourceId?: string, sourceProduct?: string, attribution?: ErrorAttributionAttributes) => RendererSyncBlockEventPayload;
|
|
52
|
+
export declare const createErrorPayload: (error: string, resourceId?: string, sourceProduct?: string, attribution?: ErrorAttributionAttributes) => SyncBlockEventPayload;
|
|
53
|
+
export declare const deleteErrorPayload: (error: string, resourceId?: string, sourceProduct?: string, attribution?: ErrorAttributionAttributes) => SyncBlockEventPayload;
|
|
15
54
|
export declare const updateCacheErrorPayload: (error: string, resourceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
16
55
|
/**
|
|
17
56
|
* Payload for `SYNCED_BLOCK_SOURCE_INFO_ORPHANED`. Fired when source-info
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
22
22
|
"@atlaskit/node-data-provider": "^13.0.0",
|
|
23
23
|
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
24
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
24
|
+
"@atlaskit/tmp-editor-statsig": "^114.0.0",
|
|
25
25
|
"@babel/runtime": "^7.0.0",
|
|
26
26
|
"@compiled/react": "^0.20.0",
|
|
27
27
|
"graphql-ws": "^5.14.2",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"uuid": "^3.1.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@atlaskit/editor-common": "^116.
|
|
33
|
+
"@atlaskit/editor-common": "^116.13.0",
|
|
34
34
|
"react": "^18.2.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"name": "@atlaskit/editor-synced-block-provider",
|
|
77
|
-
"version": "8.1.
|
|
77
|
+
"version": "8.1.9",
|
|
78
78
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
79
79
|
"author": "Atlassian Pty Ltd",
|
|
80
80
|
"license": "Apache-2.0",
|
|
@@ -85,6 +85,9 @@
|
|
|
85
85
|
"platform_editor_blocks_patch_2": {
|
|
86
86
|
"type": "boolean"
|
|
87
87
|
},
|
|
88
|
+
"platform_editor_blocks_patch_3": {
|
|
89
|
+
"type": "boolean"
|
|
90
|
+
},
|
|
88
91
|
"platform_synced_block_patch_13": {
|
|
89
92
|
"type": "boolean"
|
|
90
93
|
},
|