@atlaskit/editor-synced-block-provider 8.2.0 → 8.3.1
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 +22 -0
- package/dist/cjs/clients/block-service/ari.js +7 -3
- package/dist/cjs/clients/block-service/blockSubscription.js +4 -2
- package/dist/cjs/clients/confluence/ari.js +3 -1
- package/dist/cjs/clients/jira/ari.js +3 -1
- package/dist/cjs/hooks/useFetchSyncBlockData.js +1 -1
- package/dist/cjs/providers/block-service/blockServiceAPI.js +11 -6
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +9 -4
- package/dist/cjs/store-manager/syncBlockBatchFetcher.js +2 -1
- package/dist/cjs/store-manager/syncBlockProviderFactoryManager.js +4 -3
- package/dist/cjs/store-manager/syncBlockSubscriptionManager.js +11 -4
- package/dist/cjs/utils/errorHandling.js +152 -6
- package/dist/cjs/utils/utils.js +2 -1
- package/dist/es2019/clients/block-service/ari.js +7 -3
- package/dist/es2019/clients/block-service/blockSubscription.js +4 -2
- package/dist/es2019/clients/confluence/ari.js +3 -1
- package/dist/es2019/clients/jira/ari.js +3 -1
- package/dist/es2019/hooks/useFetchSyncBlockData.js +2 -2
- package/dist/es2019/providers/block-service/blockServiceAPI.js +15 -5
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +10 -5
- package/dist/es2019/store-manager/syncBlockBatchFetcher.js +3 -2
- package/dist/es2019/store-manager/syncBlockProviderFactoryManager.js +5 -4
- package/dist/es2019/store-manager/syncBlockSubscriptionManager.js +12 -5
- package/dist/es2019/utils/errorHandling.js +167 -22
- package/dist/es2019/utils/utils.js +2 -1
- package/dist/esm/clients/block-service/ari.js +7 -3
- package/dist/esm/clients/block-service/blockSubscription.js +4 -2
- package/dist/esm/clients/confluence/ari.js +3 -1
- package/dist/esm/clients/jira/ari.js +3 -1
- package/dist/esm/hooks/useFetchSyncBlockData.js +2 -2
- package/dist/esm/providers/block-service/blockServiceAPI.js +11 -6
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +10 -5
- package/dist/esm/store-manager/syncBlockBatchFetcher.js +3 -2
- package/dist/esm/store-manager/syncBlockProviderFactoryManager.js +5 -4
- package/dist/esm/store-manager/syncBlockSubscriptionManager.js +12 -5
- package/dist/esm/utils/errorHandling.js +149 -5
- package/dist/esm/utils/utils.js +2 -1
- package/dist/types/providers/types.d.ts +8 -0
- package/dist/types/utils/errorHandling.d.ts +94 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 8.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`73743eb8b36e6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/73743eb8b36e6) -
|
|
8
|
+
CLeanup prefer static regex violations
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 8.3.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`426eef1e61679`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/426eef1e61679) -
|
|
16
|
+
Add failure `reason`, HTTP `statusCode` and `benign` attributes to synced block fetch/subscribe
|
|
17
|
+
operational error analytics, so fetch failures can be broken down by cause (benign source-gone /
|
|
18
|
+
permission-denied vs genuine system failures) instead of regex-matching free text. Gated behind
|
|
19
|
+
`platform_editor_blocks_patch_3`; gate-off behaviour is unchanged.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
|
|
3
25
|
## 8.2.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
|
@@ -6,6 +6,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getProductFromSourceAri = exports.getLocalIdFromBlockResourceId = exports.generateBlockAriFromReference = exports.generateBlockAri = void 0;
|
|
7
7
|
/* eslint-disable require-unicode-regexp */
|
|
8
8
|
|
|
9
|
+
var GET_LOCAL_ID_FROM_BLOCK_RESOURCE_ID_REGEX = /ari:cloud:blocks:[^:]+:synced-block\/([a-zA-Z0-9-]+)/;
|
|
10
|
+
var JIRA_SOURCE_ARI_REGEX = /ari:cloud:jira:.*/;
|
|
11
|
+
var CONFLUENCE_SOURCE_ARI_REGEX = /ari:cloud:confluence:.*/;
|
|
12
|
+
|
|
9
13
|
/**
|
|
10
14
|
* Generates the block ARI from the source page ARI and the source block's resource ID.
|
|
11
15
|
* @param cloudId - the cloudId of the block. E.G the cloudId of the confluence page, or the cloudId of the Jira instance
|
|
@@ -42,18 +46,18 @@ var generateBlockAriFromReference = exports.generateBlockAriFromReference = func
|
|
|
42
46
|
* @returns the localId of the block node. A randomly generated UUID
|
|
43
47
|
*/
|
|
44
48
|
var getLocalIdFromBlockResourceId = exports.getLocalIdFromBlockResourceId = function getLocalIdFromBlockResourceId(ari) {
|
|
45
|
-
var match = ari.match(
|
|
49
|
+
var match = ari.match(GET_LOCAL_ID_FROM_BLOCK_RESOURCE_ID_REGEX);
|
|
46
50
|
if (match !== null && match !== void 0 && match[1]) {
|
|
47
51
|
return match[1];
|
|
48
52
|
}
|
|
49
53
|
throw new Error("Invalid block ARI: ".concat(ari));
|
|
50
54
|
};
|
|
51
55
|
var getProductFromSourceAri = exports.getProductFromSourceAri = function getProductFromSourceAri(ari) {
|
|
52
|
-
var jiraMatch = ari === null || ari === void 0 ? void 0 : ari.search(
|
|
56
|
+
var jiraMatch = ari === null || ari === void 0 ? void 0 : ari.search(JIRA_SOURCE_ARI_REGEX);
|
|
53
57
|
if (jiraMatch !== -1) {
|
|
54
58
|
return 'jira-work-item';
|
|
55
59
|
}
|
|
56
|
-
var confluenceMatch = ari === null || ari === void 0 ? void 0 : ari.search(
|
|
60
|
+
var confluenceMatch = ari === null || ari === void 0 ? void 0 : ari.search(CONFLUENCE_SOURCE_ARI_REGEX);
|
|
57
61
|
if (confluenceMatch !== -1) {
|
|
58
62
|
return 'confluence-page';
|
|
59
63
|
}
|
|
@@ -10,6 +10,9 @@ var _graphqlWs = require("graphql-ws");
|
|
|
10
10
|
var _coreUtils = require("@atlaskit/editor-common/core-utils");
|
|
11
11
|
var _utils = require("../../utils/utils");
|
|
12
12
|
var GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
15
|
+
var EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX = /ari:cloud:blocks:[^:]+:synced-block\/(.+)$/;
|
|
13
16
|
var blockServiceClient = null;
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -129,8 +132,7 @@ var extractGraphQLWSErrorMessage = exports.extractGraphQLWSErrorMessage = functi
|
|
|
129
132
|
* @returns The resourceId portion of the ARI
|
|
130
133
|
*/
|
|
131
134
|
var extractResourceIdFromBlockAri = function extractResourceIdFromBlockAri(blockAri) {
|
|
132
|
-
|
|
133
|
-
var match = blockAri.match(/ari:cloud:blocks:[^:]+:synced-block\/(.+)$/);
|
|
135
|
+
var match = blockAri.match(EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX);
|
|
134
136
|
return (match === null || match === void 0 ? void 0 : match[1]) || null;
|
|
135
137
|
};
|
|
136
138
|
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getPageIdAndTypeFromConfluencePageAri = exports.getConfluencePageAri = void 0;
|
|
7
7
|
/* eslint-disable require-unicode-regexp */
|
|
8
8
|
|
|
9
|
+
var CONFLUENCE_PAGE_ARI_REGEX = /ari:cloud:confluence:[^:]+:(page|blogpost)\/(\d+)/;
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* The type of the Confluence page
|
|
11
13
|
*/
|
|
@@ -31,7 +33,7 @@ var getConfluencePageAri = exports.getConfluencePageAri = function getConfluence
|
|
|
31
33
|
*/
|
|
32
34
|
var getPageIdAndTypeFromConfluencePageAri = exports.getPageIdAndTypeFromConfluencePageAri = function getPageIdAndTypeFromConfluencePageAri(_ref2) {
|
|
33
35
|
var ari = _ref2.ari;
|
|
34
|
-
var match = ari.match(
|
|
36
|
+
var match = ari.match(CONFLUENCE_PAGE_ARI_REGEX);
|
|
35
37
|
if (match !== null && match !== void 0 && match[2]) {
|
|
36
38
|
return {
|
|
37
39
|
type: match[1],
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getJiraWorkItemIdFromAri = exports.getJiraWorkItemAri = void 0;
|
|
7
7
|
/* eslint-disable require-unicode-regexp */
|
|
8
8
|
|
|
9
|
+
var JIRA_WORK_ITEM_ARI_REGEX = /ari:cloud:jira:([^:]+):issue\/(\d+)/;
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Generates the Jira work item ARI
|
|
11
13
|
* @param workItemId - the ID of the work item
|
|
@@ -25,7 +27,7 @@ var getJiraWorkItemAri = exports.getJiraWorkItemAri = function getJiraWorkItemAr
|
|
|
25
27
|
*/
|
|
26
28
|
var getJiraWorkItemIdFromAri = exports.getJiraWorkItemIdFromAri = function getJiraWorkItemIdFromAri(_ref2) {
|
|
27
29
|
var ari = _ref2.ari;
|
|
28
|
-
var match = ari.match(
|
|
30
|
+
var match = ari.match(JIRA_WORK_ITEM_ARI_REGEX);
|
|
29
31
|
if (match !== null && match !== void 0 && match[2]) {
|
|
30
32
|
return match[2];
|
|
31
33
|
}
|
|
@@ -105,7 +105,7 @@ var useFetchSyncBlockData = exports.useFetchSyncBlockData = function useFetchSyn
|
|
|
105
105
|
(0, _monitoring.logException)(_t, {
|
|
106
106
|
location: 'editor-synced-block-provider/useFetchSyncBlockData'
|
|
107
107
|
});
|
|
108
|
-
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 || fireAnalyticsEvent((0, _errorHandling.fetchErrorPayload)(_t.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
108
|
+
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 || fireAnalyticsEvent((0, _errorHandling.fetchErrorPayload)(_t.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), _t.message)));
|
|
109
109
|
|
|
110
110
|
// Set error state if fetching fails
|
|
111
111
|
setFetchState({
|
|
@@ -7,9 +7,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
});
|
|
8
8
|
exports.writeDataBatch = exports.useMemoizedBlockServiceFetchOnlyAPIProvider = exports.useMemoizedBlockServiceAPIProviders = exports.fetchReferences = exports.extractResourceIdFromBlockAri = exports.convertToSyncBlockData = exports.blockAriToResourceId = exports.batchFetchData = void 0;
|
|
9
9
|
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
10
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
10
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
12
11
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
12
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
13
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
14
14
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
15
15
|
var _react = require("react");
|
|
@@ -26,6 +26,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
26
26
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
27
27
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
28
28
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /* eslint-disable require-unicode-regexp */
|
|
29
|
+
var BLOCK_ARI_TO_RESOURCE_ID_REGEX = /^ari:cloud:blocks:.*:synced-block\/(.+)$/;
|
|
30
|
+
var EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX = /ari:cloud:blocks:[^:]+:synced-block\/(.+)$/;
|
|
29
31
|
var mapBlockError = function mapBlockError(error) {
|
|
30
32
|
switch (error.status) {
|
|
31
33
|
case 400:
|
|
@@ -92,7 +94,7 @@ var blockAriToResourceId = exports.blockAriToResourceId = function blockAriToRes
|
|
|
92
94
|
// The regex captures the full path after synced-block/
|
|
93
95
|
// e.g. ari:cloud:blocks:DUMMY-a5a01d21-1cc3-4f29-9565-f2bb8cd969f5:synced-block/confluence-page/455232061495/e8cf64e3-1b6e-489b-ad86-8465b0905bb4
|
|
94
96
|
// should return confluence-page/455232061495/e8cf64e3-1b6e-489b-ad86-8465b0905bb4
|
|
95
|
-
var match = blockAri.match(
|
|
97
|
+
var match = blockAri.match(BLOCK_ARI_TO_RESOURCE_ID_REGEX);
|
|
96
98
|
return (match === null || match === void 0 ? void 0 : match[1]) || null;
|
|
97
99
|
};
|
|
98
100
|
|
|
@@ -201,7 +203,7 @@ var fetchReferences = exports.fetchReferences = /*#__PURE__*/function () {
|
|
|
201
203
|
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
202
204
|
*/
|
|
203
205
|
var extractResourceIdFromBlockAri = exports.extractResourceIdFromBlockAri = function extractResourceIdFromBlockAri(blockAri) {
|
|
204
|
-
var match = blockAri.match(
|
|
206
|
+
var match = blockAri.match(EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX);
|
|
205
207
|
return match === null || match === void 0 ? void 0 : match[1];
|
|
206
208
|
};
|
|
207
209
|
|
|
@@ -413,10 +415,12 @@ var _batchFetchData = exports.batchFetchData = /*#__PURE__*/function () {
|
|
|
413
415
|
case 21:
|
|
414
416
|
return _context2.abrupt("return", blockNodeIdentifiers.map(function (blockNodeIdentifier) {
|
|
415
417
|
return {
|
|
416
|
-
error: {
|
|
418
|
+
error: _objectSpread({
|
|
417
419
|
type: _t4 instanceof _blockService.BlockError ? mapBlockError(_t4) : _types.SyncBlockError.Errored,
|
|
418
420
|
reason: _t4.message
|
|
419
|
-
},
|
|
421
|
+
}, _t4 instanceof _blockService.BlockError && {
|
|
422
|
+
statusCode: _t4.status
|
|
423
|
+
}),
|
|
420
424
|
resourceId: blockNodeIdentifier.resourceId
|
|
421
425
|
};
|
|
422
426
|
}));
|
|
@@ -668,7 +672,8 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
|
|
|
668
672
|
return _context5.abrupt("return", {
|
|
669
673
|
error: {
|
|
670
674
|
type: mapBlockError(_t7),
|
|
671
|
-
reason: _t7.message
|
|
675
|
+
reason: _t7.message,
|
|
676
|
+
statusCode: _t7.status
|
|
672
677
|
},
|
|
673
678
|
resourceId: resourceId
|
|
674
679
|
});
|
|
@@ -609,10 +609,12 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
609
609
|
data.forEach(function (syncBlockInstance) {
|
|
610
610
|
var _resolvedSyncBlockIns;
|
|
611
611
|
if (!syncBlockInstance.resourceId) {
|
|
612
|
-
var _syncBlockInstance$er, _syncBlockInstance$er2, _this5$fireAnalyticsE;
|
|
612
|
+
var _syncBlockInstance$er, _syncBlockInstance$er2, _this5$fireAnalyticsE, _syncBlockInstance$er3, _syncBlockInstance$er4, _syncBlockInstance$er5;
|
|
613
613
|
var payload = ((_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.reason) || ((_syncBlockInstance$er2 = syncBlockInstance.error) === null || _syncBlockInstance$er2 === void 0 ? void 0 : _syncBlockInstance$er2.type) || 'Returned sync block instance does not have resource id';
|
|
614
614
|
// No resourceId means we cannot derive a sourceProduct here; intentionally omit.
|
|
615
|
-
|
|
615
|
+
// Classify on the structured `type` first, falling back to the free-text
|
|
616
|
+
// `reason`/payload (EDITOR-7862).
|
|
617
|
+
(_this5$fireAnalyticsE = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE === void 0 || _this5$fireAnalyticsE.call(_this5, (0, _errorHandling.fetchErrorPayload)(payload, undefined, undefined, (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), ((_syncBlockInstance$er3 = syncBlockInstance.error) === null || _syncBlockInstance$er3 === void 0 ? void 0 : _syncBlockInstance$er3.type) || ((_syncBlockInstance$er4 = syncBlockInstance.error) === null || _syncBlockInstance$er4 === void 0 ? void 0 : _syncBlockInstance$er4.reason) || payload, (_syncBlockInstance$er5 = syncBlockInstance.error) === null || _syncBlockInstance$er5 === void 0 ? void 0 : _syncBlockInstance$er5.statusCode)));
|
|
616
618
|
return;
|
|
617
619
|
}
|
|
618
620
|
var existingSyncBlock = _this5.getFromCache(syncBlockInstance.resourceId);
|
|
@@ -646,7 +648,10 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
646
648
|
var isRetryingEntityNotFound = syncBlockInstance.error.type === _types.SyncBlockError.EntityNotFound && ((_this5$entityNotFound = _this5.entityNotFoundRetryCount.get(syncBlockInstance.resourceId)) !== null && _this5$entityNotFound !== void 0 ? _this5$entityNotFound : 0) < ENTITY_NOT_FOUND_MAX_RETRIES && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_13');
|
|
647
649
|
if (!isRetryingEntityNotFound) {
|
|
648
650
|
var _this5$fireAnalyticsE2, _syncBlockInstance$da, _syncBlockInstance$da2;
|
|
649
|
-
|
|
651
|
+
// Classify on the structured `type` (a `SyncBlockError` enum value) first,
|
|
652
|
+
// falling back to the free-text `reason` so source-state/permission strings
|
|
653
|
+
// are still bucketed (EDITOR-7862). The emitted `error` attribute is unchanged.
|
|
654
|
+
(_this5$fireAnalyticsE2 = _this5.fireAnalyticsEvent) === null || _this5$fireAnalyticsE2 === void 0 || _this5$fireAnalyticsE2.call(_this5, (0, _errorHandling.fetchErrorPayload)(syncBlockInstance.error.reason || syncBlockInstance.error.type, syncBlockInstance.resourceId, (_syncBlockInstance$da = (_syncBlockInstance$da2 = syncBlockInstance.data) === null || _syncBlockInstance$da2 === void 0 ? void 0 : _syncBlockInstance$da2.product) !== null && _syncBlockInstance$da !== void 0 ? _syncBlockInstance$da : (0, _utils.getSourceProductFromResourceIdSafe)(syncBlockInstance.resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), syncBlockInstance.error.type || syncBlockInstance.error.reason, syncBlockInstance.error.statusCode)));
|
|
650
655
|
}
|
|
651
656
|
if (syncBlockInstance.error.type === _types.SyncBlockError.NotFound || syncBlockInstance.error.type === _types.SyncBlockError.Forbidden) {
|
|
652
657
|
hasExpectedError = true;
|
|
@@ -970,7 +975,7 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
970
975
|
(0, _monitoring.logException)(error, {
|
|
971
976
|
location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
|
|
972
977
|
});
|
|
973
|
-
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 || _this$fireAnalyticsEv7.call(this, (0, _errorHandling.fetchErrorPayload)(error.message));
|
|
978
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 || _this$fireAnalyticsEv7.call(this, (0, _errorHandling.fetchErrorPayload)(error.message, undefined, undefined, (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
|
|
974
979
|
return function () {};
|
|
975
980
|
}
|
|
976
981
|
}
|
|
@@ -75,9 +75,10 @@ var SyncBlockBatchFetcher = exports.SyncBlockBatchFetcher = /*#__PURE__*/functio
|
|
|
75
75
|
(0, _monitoring.logException)(error, {
|
|
76
76
|
location: 'editor-synced-block-provider/syncBlockBatchFetcher/batchedFetchSyncBlocks'
|
|
77
77
|
});
|
|
78
|
+
var attribution = (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message);
|
|
78
79
|
resourceIds.forEach(function (resId) {
|
|
79
80
|
var _this$deps$getFireAna;
|
|
80
|
-
(_this$deps$getFireAna = _this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(error.message, resId, (0, _utils.getSourceProductFromResourceIdSafe)(resId)));
|
|
81
|
+
(_this$deps$getFireAna = _this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(error.message, resId, (0, _utils.getSourceProductFromResourceIdSafe)(resId), attribution));
|
|
81
82
|
});
|
|
82
83
|
}).finally(function () {
|
|
83
84
|
// If the fetcher was destroyed while the request was in flight,
|
|
@@ -10,6 +10,7 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
|
|
|
10
10
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
11
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
12
12
|
var _providerFactory = require("@atlaskit/editor-common/provider-factory");
|
|
13
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
14
|
var _errorHandling = require("../utils/errorHandling");
|
|
14
15
|
var _resourceId = require("../utils/resourceId");
|
|
15
16
|
var _utils = require("../utils/utils");
|
|
@@ -33,7 +34,7 @@ var SyncBlockProviderFactoryManager = exports.SyncBlockProviderFactoryManager =
|
|
|
33
34
|
(0, _monitoring.logException)(error, {
|
|
34
35
|
location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
|
|
35
36
|
});
|
|
36
|
-
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
37
|
+
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
|
|
37
38
|
return undefined;
|
|
38
39
|
}
|
|
39
40
|
var _dataProvider$getSync = dataProvider.getSyncedBlockRendererProviderOptions(),
|
|
@@ -66,7 +67,7 @@ var SyncBlockProviderFactoryManager = exports.SyncBlockProviderFactoryManager =
|
|
|
66
67
|
(0, _monitoring.logException)(error, {
|
|
67
68
|
location: 'editor-synced-block-provider/syncBlockProviderFactoryManager'
|
|
68
69
|
});
|
|
69
|
-
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
70
|
+
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
return providerFactory;
|
|
@@ -151,7 +152,7 @@ var SyncBlockProviderFactoryManager = exports.SyncBlockProviderFactoryManager =
|
|
|
151
152
|
if (!syncBlock.data.sourceAri || !syncBlock.data.product) {
|
|
152
153
|
var _this$deps$getFireAna3, _syncBlock$data$produ;
|
|
153
154
|
(_this$deps$getFireAna3 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna3 === void 0 || _this$deps$getFireAna3((0, _errorHandling.fetchErrorPayload)('Sync block source ari or product not found', resourceId, // Prefer cached product when available; fall back to parsing resourceId.
|
|
154
|
-
(_syncBlock$data$produ = syncBlock.data.product) !== null && _syncBlock$data$produ !== void 0 ? _syncBlock$data$produ : (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
155
|
+
(_syncBlock$data$produ = syncBlock.data.product) !== null && _syncBlock$data$produ !== void 0 ? _syncBlock$data$produ : (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), 'Sync block source ari or product not found')));
|
|
155
156
|
return;
|
|
156
157
|
}
|
|
157
158
|
var parentInfo = dataProvider.retrieveSyncBlockParentInfo(syncBlock.data.sourceAri, syncBlock.data.product);
|
|
@@ -120,7 +120,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
120
120
|
(0, _monitoring.logException)(error, {
|
|
121
121
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/notifySubscriptionChangeListeners'
|
|
122
122
|
});
|
|
123
|
-
(_this2$deps$getFireAn = _this2.deps.getFireAnalyticsEvent()) === null || _this2$deps$getFireAn === void 0 || _this2$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message));
|
|
123
|
+
(_this2$deps$getFireAn = _this2.deps.getFireAnalyticsEvent()) === null || _this2$deps$getFireAn === void 0 || _this2$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message, undefined, undefined, (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), error.message)));
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
}
|
|
@@ -305,6 +305,9 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
305
305
|
// recovers on reconnect, so under the gate we don't fire a
|
|
306
306
|
// user-facing error here — it's only surfaced on exhaustion (see
|
|
307
307
|
// scheduleReconnection). Gate OFF keeps the legacy fire-on-drop.
|
|
308
|
+
// This branch only runs when the gate is OFF, so buildFetchErrorAttribution
|
|
309
|
+
// would return undefined; the structured attribution (EDITOR-7862) is therefore
|
|
310
|
+
// applied at the gate-ON exhaustion site in scheduleReconnection instead.
|
|
308
311
|
if (!(0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3')) {
|
|
309
312
|
var _this5$deps$getFireAn;
|
|
310
313
|
(_this5$deps$getFireAn = _this5.deps.getFireAnalyticsEvent()) === null || _this5$deps$getFireAn === void 0 || _this5$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
@@ -356,7 +359,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
356
359
|
(0, _monitoring.logException)(new Error(errorMessage), {
|
|
357
360
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/max-retries-exhausted'
|
|
358
361
|
});
|
|
359
|
-
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId)));
|
|
362
|
+
(_this$deps$getFireAna = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna === void 0 || _this$deps$getFireAna((0, _errorHandling.fetchErrorPayload)(errorMessage, resourceId, (0, _utils.getSourceProductFromResourceIdSafe)(resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), errorMessage)));
|
|
360
363
|
return;
|
|
361
364
|
}
|
|
362
365
|
var delay = this.getReconnectionDelay(attempts);
|
|
@@ -510,9 +513,13 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
510
513
|
});
|
|
511
514
|
this.deps.fetchSyncBlockSourceInfo(resolved.resourceId);
|
|
512
515
|
} else {
|
|
513
|
-
var _syncBlockInstance$er, _syncBlockInstance$er2, _this$deps$getFireAna2, _syncBlockInstance$da3, _syncBlockInstance$da4;
|
|
516
|
+
var _syncBlockInstance$er, _syncBlockInstance$er2, _this$deps$getFireAna2, _syncBlockInstance$da3, _syncBlockInstance$da4, _syncBlockInstance$er3, _syncBlockInstance$er4, _syncBlockInstance$er5;
|
|
514
517
|
var errorMessage = ((_syncBlockInstance$er = syncBlockInstance.error) === null || _syncBlockInstance$er === void 0 ? void 0 : _syncBlockInstance$er.reason) || ((_syncBlockInstance$er2 = syncBlockInstance.error) === null || _syncBlockInstance$er2 === void 0 ? void 0 : _syncBlockInstance$er2.type);
|
|
515
|
-
|
|
518
|
+
|
|
519
|
+
// Prefer the structured `type` (a `SyncBlockError` enum value) for classification
|
|
520
|
+
// and fall back to the free-text `reason` so source-state/permission strings are
|
|
521
|
+
// still bucketed (EDITOR-7862). The emitted free-text `error` attribute is unchanged.
|
|
522
|
+
(_this$deps$getFireAna2 = this.deps.getFireAnalyticsEvent()) === null || _this$deps$getFireAna2 === void 0 || _this$deps$getFireAna2((0, _errorHandling.fetchErrorPayload)(errorMessage, syncBlockInstance.resourceId, (_syncBlockInstance$da3 = (_syncBlockInstance$da4 = syncBlockInstance.data) === null || _syncBlockInstance$da4 === void 0 ? void 0 : _syncBlockInstance$da4.product) !== null && _syncBlockInstance$da3 !== void 0 ? _syncBlockInstance$da3 : (0, _utils.getSourceProductFromResourceIdSafe)(syncBlockInstance.resourceId), (0, _errorHandling.buildFetchErrorAttribution)((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_3'), ((_syncBlockInstance$er3 = syncBlockInstance.error) === null || _syncBlockInstance$er3 === void 0 ? void 0 : _syncBlockInstance$er3.type) || ((_syncBlockInstance$er4 = syncBlockInstance.error) === null || _syncBlockInstance$er4 === void 0 ? void 0 : _syncBlockInstance$er4.reason), (_syncBlockInstance$er5 = syncBlockInstance.error) === null || _syncBlockInstance$er5 === void 0 ? void 0 : _syncBlockInstance$er5.statusCode)));
|
|
516
523
|
}
|
|
517
524
|
}
|
|
518
525
|
}]);
|
|
@@ -4,7 +4,10 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.
|
|
7
|
+
exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayloadNew = exports.createSuccessPayload = exports.createErrorPayload = exports.classifyFetchErrorReason = exports.classifyErrorReason = exports.cacheDeletionForcedPayload = exports.buildFetchErrorAttribution = exports.buildErrorAttribution = exports.FETCH_BENIGN_REASONS = void 0;
|
|
8
|
+
exports.getErrorPayload = getErrorPayload;
|
|
9
|
+
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = void 0;
|
|
10
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
8
11
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
12
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
10
13
|
var _types = require("../common/types");
|
|
@@ -67,19 +70,155 @@ var buildErrorAttribution = exports.buildErrorAttribution = function buildErrorA
|
|
|
67
70
|
});
|
|
68
71
|
};
|
|
69
72
|
|
|
73
|
+
/**
|
|
74
|
+
* The set of categorical failure reasons emitted on synced-block fetch/subscribe
|
|
75
|
+
* operational error events (EDITOR-7862). Extends the write-path
|
|
76
|
+
* {@link SyncBlockErrorReason} set with read-path-specific buckets so the analytics
|
|
77
|
+
* dashboard can break fetch failures down by cause instead of regex-matching the
|
|
78
|
+
* free-text `error` blob.
|
|
79
|
+
*
|
|
80
|
+
* The read path surfaces several causes the write-path `SyncBlockError` enum does not
|
|
81
|
+
* model (benign source-state transitions, permission denials, WebSocket lifecycle, and
|
|
82
|
+
* client-side readiness errors), so those are added here. Known `SyncBlockError` enum
|
|
83
|
+
* values (`not_found`, `forbidden`, ...) still pass through unchanged.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fetch reasons that represent benign (working-as-designed) outcomes rather than genuine
|
|
88
|
+
* system failures. The dashboard uses this to compute a "true error rate" by excluding
|
|
89
|
+
* benign reasons via the structured `reason` attribute instead of brittle free-text regex
|
|
90
|
+
* (EDITOR-7862).
|
|
91
|
+
*/
|
|
92
|
+
var FETCH_BENIGN_REASONS = exports.FETCH_BENIGN_REASONS = new Set(['source_deleted', 'source_unpublished', 'source_unsynced', 'source_not_found', 'permission_denied', 'unauthenticated',
|
|
93
|
+
// Mirror the benign write-path enum equivalents so already-classified errors are
|
|
94
|
+
// treated consistently.
|
|
95
|
+
'not_found', 'forbidden', 'unpublished'
|
|
96
|
+
// NOTE: `entity_not_found` and `offline` are intentionally NOT benign.
|
|
97
|
+
// - `entity_not_found` is retried up to ENTITY_NOT_FOUND_MAX_RETRIES (analytics are
|
|
98
|
+
// suppressed during retries); by the time the error event fires, retries are
|
|
99
|
+
// exhausted and it is a genuine failure.
|
|
100
|
+
// - `offline` is genuine client connectivity loss, not a working-as-designed outcome.
|
|
101
|
+
// Both still emit `reason` (so they can be inspected) but are counted as true errors.
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Ordered list of substring matchers mapping known free-text fetch/subscribe error
|
|
106
|
+
* strings to a categorical {@link SyncBlockFetchErrorReason}. Each entry is a list of
|
|
107
|
+
* lowercase needles; if ANY needle is found (case-insensitively) in the raw `error`
|
|
108
|
+
* string, the entry's reason is used. Order matters: more specific entries must precede
|
|
109
|
+
* more general ones.
|
|
110
|
+
*
|
|
111
|
+
* Plain `String.includes` substring matching is used deliberately instead of `RegExp`:
|
|
112
|
+
* it sidesteps the `require-unicode-regexp` lint rule AND the declaration build's lower
|
|
113
|
+
* compile target (which rejects the regex `u` flag with TS1501), while being faster and
|
|
114
|
+
* easier to read. Doing this classification in code (rather than in dashboard SQL) keeps
|
|
115
|
+
* the buckets versioned with the source strings that produce them (EDITOR-7862).
|
|
116
|
+
*/
|
|
117
|
+
var FETCH_REASON_MATCHERS = [
|
|
118
|
+
// Benign: source-state transitions (deletionReason values + free text).
|
|
119
|
+
[['source-document-deleted'], 'source_deleted'], [['source-block-deleted'], 'source_deleted'], [['source-block-unpublished'], 'source_unpublished'], [['source-block-unsynced'], 'source_unsynced'], [['does not exist'], 'source_not_found'], [['unpublished'], 'source_unpublished'],
|
|
120
|
+
// Working-as-designed permission outcomes.
|
|
121
|
+
[['unauthenticated'], 'unauthenticated'], [['not permitted to read synced block'], 'permission_denied'], [['bulk permission check failed'], 'permission_denied'], [['permission denied'], 'permission_denied'],
|
|
122
|
+
// Genuine system failures.
|
|
123
|
+
[['reconnection failed after'], 'websocket_exhausted'],
|
|
124
|
+
// `reconnect to the subscription` (not a bare `reconnect`) avoids false positives on
|
|
125
|
+
// unrelated DB/GraphQL "reconnect" messages while still matching the WS-drop string.
|
|
126
|
+
[['websocket', 'reconnect to the subscription', 'subscription terminated'], 'websocket_drop'], [['429', 'rate limit', 'rate-limit', 'ratelimit'], 'rate_limited'],
|
|
127
|
+
// `econnrefused`/`econnreset` (not a bare `econn`, which would also match `reconnect`).
|
|
128
|
+
[['network', 'failed to fetch', 'econnrefused', 'econnreset', 'timed out', 'timeout'], 'network'], [['data provider not set'], 'data_provider_not_ready']];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Maps a fetch/subscribe `error` field — which may be a {@link SyncBlockError} enum
|
|
132
|
+
* value, a {@link DeletionReason} value, or an arbitrary free-text/JSON blob — to a
|
|
133
|
+
* stable categorical {@link SyncBlockFetchErrorReason} for analytics grouping.
|
|
134
|
+
*
|
|
135
|
+
* Resolution order:
|
|
136
|
+
* 1. Known `SyncBlockError` enum value → passed through (via {@link classifyErrorReason}).
|
|
137
|
+
* 2. Known free-text substring → mapped to a fetch-specific bucket.
|
|
138
|
+
* 3. Anything else (including opaque blobs like `errored`-only payloads or
|
|
139
|
+
* `ErrorEvent: "undefined"`) → `'unknown'`, so the dashboard never groups on free text.
|
|
140
|
+
*
|
|
141
|
+
* Note: the bare string `'errored'` IS a `SyncBlockError` enum value and therefore
|
|
142
|
+
* classifies as `'errored'` (not `'unknown'`); only genuinely unrecognised strings
|
|
143
|
+
* collapse to `'unknown'`.
|
|
144
|
+
*/
|
|
145
|
+
var classifyFetchErrorReason = exports.classifyFetchErrorReason = function classifyFetchErrorReason(error) {
|
|
146
|
+
var enumReason = classifyErrorReason(error);
|
|
147
|
+
if (enumReason !== 'unknown') {
|
|
148
|
+
return enumReason;
|
|
149
|
+
}
|
|
150
|
+
if (error) {
|
|
151
|
+
var haystack = error.toLowerCase();
|
|
152
|
+
for (var _i = 0, _FETCH_REASON_MATCHER = FETCH_REASON_MATCHERS; _i < _FETCH_REASON_MATCHER.length; _i++) {
|
|
153
|
+
var _FETCH_REASON_MATCHER2 = (0, _slicedToArray2.default)(_FETCH_REASON_MATCHER[_i], 2),
|
|
154
|
+
needles = _FETCH_REASON_MATCHER2[0],
|
|
155
|
+
reason = _FETCH_REASON_MATCHER2[1];
|
|
156
|
+
if (needles.some(function (needle) {
|
|
157
|
+
return haystack.includes(needle);
|
|
158
|
+
})) {
|
|
159
|
+
return reason;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return 'unknown';
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Extra, optional analytics attributes describing WHY a fetch/subscribe synced-block
|
|
168
|
+
* action failed. Spread conditionally so we never emit `undefined` keys (EDITOR-7862).
|
|
169
|
+
*
|
|
170
|
+
* Adds `benign` on top of the shared {@link ErrorAttributionAttributes} so the dashboard
|
|
171
|
+
* can compute a true error rate (`genuine / total`) without any free-text regex.
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Builds the {@link FetchErrorAttributionAttributes} for a failed fetch/subscribe
|
|
176
|
+
* synced-block operation from the raw `error` field and optional backend `statusCode`.
|
|
177
|
+
* Returns `undefined` when the `platform_editor_blocks_patch_3` gate is OFF, so the new
|
|
178
|
+
* `reason`/`statusCode`/`benign` attributes are only emitted once the gate is rolled out
|
|
179
|
+
* (EDITOR-7862). The existing free-text `error` attribute is always left unchanged.
|
|
180
|
+
*
|
|
181
|
+
* `gateEnabled` is injected by the caller (the store managers evaluate `fg(...)`) so this
|
|
182
|
+
* helper stays pure and trivially unit-testable for both gate states.
|
|
183
|
+
*/
|
|
184
|
+
var buildFetchErrorAttribution = exports.buildFetchErrorAttribution = function buildFetchErrorAttribution(gateEnabled, error, statusCode) {
|
|
185
|
+
if (!gateEnabled) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
var reason = classifyFetchErrorReason(error);
|
|
189
|
+
return _objectSpread({
|
|
190
|
+
reason: reason,
|
|
191
|
+
benign: FETCH_BENIGN_REASONS.has(reason)
|
|
192
|
+
}, statusCode !== undefined && {
|
|
193
|
+
statusCode: statusCode
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
70
197
|
// `sourceProduct` is threaded through every helper so analytics
|
|
71
198
|
// events can be attributed to the source product (`confluence-page` vs `jira-work-item`).
|
|
72
199
|
// All helpers accept it as an optional trailing argument; the spread-only-when-truthy
|
|
73
200
|
// pattern below ensures we never emit `sourceProduct: undefined` (which would dirty the
|
|
74
201
|
// event schema with empty keys).
|
|
75
202
|
|
|
76
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Shared operational ERROR payload builder for synced-block events.
|
|
205
|
+
*
|
|
206
|
+
* Overloaded so the emitted `reason` type stays accurate per path (raised in review on
|
|
207
|
+
* EDITOR-7862): fetch/subscribe callers pass a {@link FetchErrorAttributionAttributes}
|
|
208
|
+
* (discriminated by its required `benign` field) and get the wider
|
|
209
|
+
* {@link SyncBlockFetchErrorReason}; write-path callers pass an
|
|
210
|
+
* {@link ErrorAttributionAttributes} (or nothing) and keep the narrower
|
|
211
|
+
* {@link SyncBlockErrorReason}, so write-path payloads never claim to carry fetch-only
|
|
212
|
+
* buckets like `source_deleted` they never emit.
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
function getErrorPayload(actionSubjectId, error, resourceId, sourceProduct, attribution) {
|
|
77
216
|
return {
|
|
78
217
|
action: _analytics.ACTION.ERROR,
|
|
79
218
|
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
|
|
80
219
|
actionSubjectId: actionSubjectId,
|
|
81
220
|
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
82
|
-
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
221
|
+
attributes: _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
83
222
|
error: error
|
|
84
223
|
}, resourceId && {
|
|
85
224
|
resourceId: resourceId
|
|
@@ -89,11 +228,18 @@ var getErrorPayload = exports.getErrorPayload = function getErrorPayload(actionS
|
|
|
89
228
|
reason: attribution.reason
|
|
90
229
|
}), (attribution === null || attribution === void 0 ? void 0 : attribution.statusCode) !== undefined && {
|
|
91
230
|
statusCode: attribution.statusCode
|
|
231
|
+
}), attribution && 'benign' in attribution && {
|
|
232
|
+
benign: attribution.benign
|
|
92
233
|
})
|
|
93
234
|
};
|
|
94
|
-
}
|
|
95
|
-
var fetchErrorPayload = exports.fetchErrorPayload = function fetchErrorPayload(error, resourceId, sourceProduct) {
|
|
96
|
-
return
|
|
235
|
+
}
|
|
236
|
+
var fetchErrorPayload = exports.fetchErrorPayload = function fetchErrorPayload(error, resourceId, sourceProduct, attribution) {
|
|
237
|
+
return (
|
|
238
|
+
// Branch on attribution presence so each call resolves to a concrete overload: with
|
|
239
|
+
// attribution it hits the fetch overload (wider `reason`); without it (gate OFF) it
|
|
240
|
+
// hits the no-attribution overload. Both produce a fetch event regardless.
|
|
241
|
+
attribution ? getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_FETCH, error, resourceId, sourceProduct, attribution) : getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_FETCH, error, resourceId, sourceProduct)
|
|
242
|
+
);
|
|
97
243
|
};
|
|
98
244
|
var getSourceInfoErrorPayload = exports.getSourceInfoErrorPayload = function getSourceInfoErrorPayload(error, resourceId, sourceProduct) {
|
|
99
245
|
return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_GET_SOURCE_INFO, error, resourceId, sourceProduct);
|
package/dist/cjs/utils/utils.js
CHANGED
|
@@ -10,6 +10,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
10
10
|
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; }
|
|
11
11
|
/* eslint-disable require-unicode-regexp */
|
|
12
12
|
|
|
13
|
+
var GET_CONTENT_ID_AND_PRODUCT_REGEX = /^(confluence-page|jira-work-item)\/([^/]+)/;
|
|
13
14
|
var _normalizeSyncBlockJSONContentInternal = function normalizeSyncBlockJSONContentInternal(content, options) {
|
|
14
15
|
var normalizedContent;
|
|
15
16
|
content.forEach(function (contentNode, index) {
|
|
@@ -109,7 +110,7 @@ var convertPMNodesToSyncBlockNodes = exports.convertPMNodesToSyncBlockNodes = fu
|
|
|
109
110
|
* Extracts the source page content id and source product
|
|
110
111
|
*/
|
|
111
112
|
var getContentIdAndProductFromResourceId = exports.getContentIdAndProductFromResourceId = function getContentIdAndProductFromResourceId(resourceId) {
|
|
112
|
-
var match = resourceId.match(
|
|
113
|
+
var match = resourceId.match(GET_CONTENT_ID_AND_PRODUCT_REGEX);
|
|
113
114
|
if (match !== null && match !== void 0 && match[2]) {
|
|
114
115
|
return {
|
|
115
116
|
sourceProduct: match[1],
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* eslint-disable require-unicode-regexp */
|
|
2
2
|
|
|
3
|
+
const GET_LOCAL_ID_FROM_BLOCK_RESOURCE_ID_REGEX = /ari:cloud:blocks:[^:]+:synced-block\/([a-zA-Z0-9-]+)/;
|
|
4
|
+
const JIRA_SOURCE_ARI_REGEX = /ari:cloud:jira:.*/;
|
|
5
|
+
const CONFLUENCE_SOURCE_ARI_REGEX = /ari:cloud:confluence:.*/;
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Generates the block ARI from the source page ARI and the source block's resource ID.
|
|
5
9
|
* @param cloudId - the cloudId of the block. E.G the cloudId of the confluence page, or the cloudId of the Jira instance
|
|
@@ -38,18 +42,18 @@ export const generateBlockAriFromReference = ({
|
|
|
38
42
|
* @returns the localId of the block node. A randomly generated UUID
|
|
39
43
|
*/
|
|
40
44
|
export const getLocalIdFromBlockResourceId = ari => {
|
|
41
|
-
const match = ari.match(
|
|
45
|
+
const match = ari.match(GET_LOCAL_ID_FROM_BLOCK_RESOURCE_ID_REGEX);
|
|
42
46
|
if (match !== null && match !== void 0 && match[1]) {
|
|
43
47
|
return match[1];
|
|
44
48
|
}
|
|
45
49
|
throw new Error(`Invalid block ARI: ${ari}`);
|
|
46
50
|
};
|
|
47
51
|
export const getProductFromSourceAri = ari => {
|
|
48
|
-
const jiraMatch = ari === null || ari === void 0 ? void 0 : ari.search(
|
|
52
|
+
const jiraMatch = ari === null || ari === void 0 ? void 0 : ari.search(JIRA_SOURCE_ARI_REGEX);
|
|
49
53
|
if (jiraMatch !== -1) {
|
|
50
54
|
return 'jira-work-item';
|
|
51
55
|
}
|
|
52
|
-
const confluenceMatch = ari === null || ari === void 0 ? void 0 : ari.search(
|
|
56
|
+
const confluenceMatch = ari === null || ari === void 0 ? void 0 : ari.search(CONFLUENCE_SOURCE_ARI_REGEX);
|
|
53
57
|
if (confluenceMatch !== -1) {
|
|
54
58
|
return 'confluence-page';
|
|
55
59
|
}
|
|
@@ -2,6 +2,9 @@ import { createClient } from 'graphql-ws';
|
|
|
2
2
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
3
3
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
4
4
|
const GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
7
|
+
const EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX = /ari:cloud:blocks:[^:]+:synced-block\/(.+)$/;
|
|
5
8
|
let blockServiceClient = null;
|
|
6
9
|
|
|
7
10
|
/**
|
|
@@ -136,8 +139,7 @@ export const extractGraphQLWSErrorMessage = error => {
|
|
|
136
139
|
* @returns The resourceId portion of the ARI
|
|
137
140
|
*/
|
|
138
141
|
const extractResourceIdFromBlockAri = blockAri => {
|
|
139
|
-
|
|
140
|
-
const match = blockAri.match(/ari:cloud:blocks:[^:]+:synced-block\/(.+)$/);
|
|
142
|
+
const match = blockAri.match(EXTRACT_RESOURCE_ID_FROM_BLOCK_ARI_REGEX);
|
|
141
143
|
return (match === null || match === void 0 ? void 0 : match[1]) || null;
|
|
142
144
|
};
|
|
143
145
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable require-unicode-regexp */
|
|
2
2
|
|
|
3
|
+
const CONFLUENCE_PAGE_ARI_REGEX = /ari:cloud:confluence:[^:]+:(page|blogpost)\/(\d+)/;
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* The type of the Confluence page
|
|
5
7
|
*/
|
|
@@ -27,7 +29,7 @@ export const getConfluencePageAri = ({
|
|
|
27
29
|
export const getPageIdAndTypeFromConfluencePageAri = ({
|
|
28
30
|
ari
|
|
29
31
|
}) => {
|
|
30
|
-
const match = ari.match(
|
|
32
|
+
const match = ari.match(CONFLUENCE_PAGE_ARI_REGEX);
|
|
31
33
|
if (match !== null && match !== void 0 && match[2]) {
|
|
32
34
|
return {
|
|
33
35
|
type: match[1],
|