@atlaskit/editor-synced-block-provider 8.5.4 → 8.5.6
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 +18 -0
- package/dist/cjs/entry-points/errorHandling.js +12 -0
- package/dist/cjs/hooks/useFetchSyncBlockData.js +6 -3
- package/dist/cjs/providers/block-service/blockServiceAPI.js +6 -6
- package/dist/cjs/utils/errorHandling.js +29 -8
- package/dist/es2019/entry-points/errorHandling.js +1 -1
- package/dist/es2019/hooks/useFetchSyncBlockData.js +7 -3
- package/dist/es2019/providers/block-service/blockServiceAPI.js +11 -6
- package/dist/es2019/utils/errorHandling.js +31 -7
- package/dist/esm/entry-points/errorHandling.js +1 -1
- package/dist/esm/hooks/useFetchSyncBlockData.js +7 -4
- package/dist/esm/providers/block-service/blockServiceAPI.js +7 -7
- package/dist/esm/utils/errorHandling.js +28 -7
- package/dist/types/entry-points/errorHandling.d.ts +1 -1
- package/dist/types/providers/types.d.ts +11 -0
- package/dist/types/utils/errorHandling.d.ts +11 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 8.5.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 8.5.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`b1ff8c881a4aa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b1ff8c881a4aa) -
|
|
14
|
+
[ux] Synced Blocks: de-opaque the renderer error-boundary fetch analytics event. The event now
|
|
15
|
+
routes through the shared fetch-error attribution builder (emitting structured
|
|
16
|
+
`reason`/`benign`/`sourceProduct`/`statusCode`, gated on `platform_editor_blocks_patch_4`) and a
|
|
17
|
+
PII-safe original error message is threaded from the upstream fetch catch so previously-opaque
|
|
18
|
+
`errored` failures can be classified and inspected.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
3
21
|
## 8.5.4
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -3,10 +3,22 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "buildFetchErrorAttribution", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _errorHandling.buildFetchErrorAttribution;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "fetchErrorPayload", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function get() {
|
|
9
15
|
return _errorHandling.fetchErrorPayload;
|
|
10
16
|
}
|
|
11
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "getPiiSafeOriginalError", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function get() {
|
|
21
|
+
return _errorHandling.getPiiSafeOriginalError;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
12
24
|
var _errorHandling = require("../utils/errorHandling");
|
|
@@ -107,13 +107,16 @@ var useFetchSyncBlockData = exports.useFetchSyncBlockData = function useFetchSyn
|
|
|
107
107
|
});
|
|
108
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
|
+
// Thread the PII-safe original message/name so the renderer can de-opaque
|
|
111
|
+
// this otherwise-bare `errored` failure. `originalMessage` carries the
|
|
112
|
+
// Error.message and is preferred over `reason` by the renderer, so no
|
|
113
|
+
// separate `reason` is set here.
|
|
111
114
|
setFetchState({
|
|
112
115
|
syncBlockInstance: {
|
|
113
116
|
resourceId: resourceId || '',
|
|
114
|
-
error: {
|
|
117
|
+
error: _objectSpread({
|
|
115
118
|
type: _types.SyncBlockError.Errored
|
|
116
|
-
}
|
|
119
|
+
}, (0, _errorHandling.getPiiSafeOriginalError)(_t))
|
|
117
120
|
},
|
|
118
121
|
isLoading: false
|
|
119
122
|
});
|
|
@@ -415,12 +415,12 @@ var _batchFetchData = exports.batchFetchData = /*#__PURE__*/function () {
|
|
|
415
415
|
case 21:
|
|
416
416
|
return _context2.abrupt("return", blockNodeIdentifiers.map(function (blockNodeIdentifier) {
|
|
417
417
|
return {
|
|
418
|
-
error: _objectSpread({
|
|
418
|
+
error: _objectSpread(_objectSpread({
|
|
419
419
|
type: _t4 instanceof _blockService.BlockError ? mapBlockError(_t4) : _types.SyncBlockError.Errored,
|
|
420
420
|
reason: _t4.message
|
|
421
421
|
}, _t4 instanceof _blockService.BlockError && {
|
|
422
422
|
statusCode: _t4.status
|
|
423
|
-
}),
|
|
423
|
+
}), (0, _errorHandling.getPiiSafeOriginalError)(_t4)),
|
|
424
424
|
resourceId: blockNodeIdentifier.resourceId
|
|
425
425
|
};
|
|
426
426
|
}));
|
|
@@ -670,19 +670,19 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
|
|
|
670
670
|
break;
|
|
671
671
|
}
|
|
672
672
|
return _context5.abrupt("return", {
|
|
673
|
-
error: {
|
|
673
|
+
error: _objectSpread({
|
|
674
674
|
type: mapBlockError(_t7),
|
|
675
675
|
reason: _t7.message,
|
|
676
676
|
statusCode: _t7.status
|
|
677
|
-
},
|
|
677
|
+
}, (0, _errorHandling.getPiiSafeOriginalError)(_t7)),
|
|
678
678
|
resourceId: resourceId
|
|
679
679
|
});
|
|
680
680
|
case 5:
|
|
681
681
|
return _context5.abrupt("return", {
|
|
682
|
-
error: {
|
|
682
|
+
error: _objectSpread({
|
|
683
683
|
type: _types.SyncBlockError.Errored,
|
|
684
684
|
reason: _t7.message
|
|
685
|
-
},
|
|
685
|
+
}, (0, _errorHandling.getPiiSafeOriginalError)(_t7)),
|
|
686
686
|
resourceId: resourceId
|
|
687
687
|
});
|
|
688
688
|
case 6:
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.createSuccessPayload = exports.createSuccessOperationalPayload = exports.createErrorPayload = exports.classifyFetchErrorReason = exports.classifyErrorReason = exports.cacheDeletionForcedPayload = exports.buildFetchErrorAttribution = exports.buildErrorAttribution = exports.FETCH_BENIGN_REASONS = void 0;
|
|
8
8
|
exports.getErrorPayload = getErrorPayload;
|
|
9
|
-
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = void 0;
|
|
9
|
+
exports.updateSuccessPayload = exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.sourceInfoOrphanedPayload = exports.getSourceInfoErrorPayload = exports.getPiiSafeOriginalError = void 0;
|
|
10
10
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
11
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
12
12
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
@@ -21,6 +21,24 @@ var stringifyError = exports.stringifyError = function stringifyError(error) {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* PII-safe extraction of `message`/`name` from an unknown caught error, used to
|
|
26
|
+
* de-opaque `errored` failures. Only ever reads `Error.message` / `Error.name` — it
|
|
27
|
+
* never stringifies arbitrary objects (which could pull in node content / UGC), and
|
|
28
|
+
* non-`Error` values yield `{}` so no `[object Object]` or serialized payload leaks
|
|
29
|
+
* into analytics.
|
|
30
|
+
*/
|
|
31
|
+
var getPiiSafeOriginalError = exports.getPiiSafeOriginalError = function getPiiSafeOriginalError(error) {
|
|
32
|
+
if (error instanceof Error) {
|
|
33
|
+
return _objectSpread(_objectSpread({}, error.message && {
|
|
34
|
+
originalMessage: error.message
|
|
35
|
+
}), error.name && {
|
|
36
|
+
originalName: error.name
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return {};
|
|
40
|
+
};
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* The set of categorical failure reasons emitted on synced-block operational error
|
|
26
44
|
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
@@ -92,13 +110,16 @@ var buildErrorAttribution = exports.buildErrorAttribution = function buildErrorA
|
|
|
92
110
|
var FETCH_BENIGN_REASONS = exports.FETCH_BENIGN_REASONS = new Set(['source_deleted', 'source_unpublished', 'source_unsynced', 'source_not_found', 'permission_denied', 'unauthenticated',
|
|
93
111
|
// Mirror the benign write-path enum equivalents so already-classified errors are
|
|
94
112
|
// treated consistently.
|
|
95
|
-
'not_found', 'forbidden', 'unpublished'
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
//
|
|
113
|
+
'not_found', 'forbidden', 'unpublished',
|
|
114
|
+
// `offline` is an expected client connectivity state, not a read-path system failure:
|
|
115
|
+
// the error component is rendered for the offline experience, so it should not inflate
|
|
116
|
+
// the true error rate. It still emits `reason` (so its volume can be inspected) but is
|
|
117
|
+
// excluded from true-error counts.
|
|
118
|
+
'offline'
|
|
119
|
+
// NOTE: `entity_not_found` is intentionally NOT benign — it is retried up to
|
|
120
|
+
// ENTITY_NOT_FOUND_MAX_RETRIES (analytics are suppressed during retries); by the time
|
|
121
|
+
// the error event fires, retries are exhausted and it is a genuine failure. It still
|
|
122
|
+
// emits `reason` (so it can be inspected) but is counted as a true error.
|
|
102
123
|
]);
|
|
103
124
|
|
|
104
125
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
|
-
export { fetchErrorPayload } from '../utils/errorHandling';
|
|
2
|
+
export { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
|
|
@@ -3,7 +3,7 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
5
|
import { isProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
6
|
-
import { buildFetchErrorAttribution, fetchErrorPayload } from '../utils/errorHandling';
|
|
6
|
+
import { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
|
|
7
7
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
8
8
|
export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalyticsEvent) => {
|
|
9
9
|
var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
|
|
@@ -74,12 +74,16 @@ export const useFetchSyncBlockData = (manager, resourceId, localId, fireAnalytic
|
|
|
74
74
|
});
|
|
75
75
|
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 ? void 0 : fireAnalyticsEvent(fetchErrorPayload(error.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), error.message)));
|
|
76
76
|
|
|
77
|
-
//
|
|
77
|
+
// Thread the PII-safe original message/name so the renderer can de-opaque
|
|
78
|
+
// this otherwise-bare `errored` failure. `originalMessage` carries the
|
|
79
|
+
// Error.message and is preferred over `reason` by the renderer, so no
|
|
80
|
+
// separate `reason` is set here.
|
|
78
81
|
setFetchState({
|
|
79
82
|
syncBlockInstance: {
|
|
80
83
|
resourceId: resourceId || '',
|
|
81
84
|
error: {
|
|
82
|
-
type: SyncBlockError.Errored
|
|
85
|
+
type: SyncBlockError.Errored,
|
|
86
|
+
...getPiiSafeOriginalError(error)
|
|
83
87
|
}
|
|
84
88
|
},
|
|
85
89
|
isLoading: false
|
|
@@ -4,7 +4,7 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
|
4
4
|
import { generateBlockAri, generateBlockAriFromReference } from '../../clients/block-service/ari';
|
|
5
5
|
import { batchRetrieveSyncedBlocks, BlockError, BlockNotFoundError, BlockTimeoutError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getReferenceSyncedBlocksByBlockAri, getSyncedBlockContent, updateReferenceSyncedBlockOnDocument, updateSyncedBlock, updateSyncedBlocks } from '../../clients/block-service/blockService';
|
|
6
6
|
import { SyncBlockError } from '../../common/types';
|
|
7
|
-
import { stringifyError } from '../../utils/errorHandling';
|
|
7
|
+
import { getPiiSafeOriginalError, stringifyError } from '../../utils/errorHandling';
|
|
8
8
|
import { createResourceIdForReference } from '../../utils/resourceId';
|
|
9
9
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
10
10
|
const BLOCK_ARI_TO_RESOURCE_ID_REGEX = /^ari:cloud:blocks:.*:synced-block\/(.+)$/;
|
|
@@ -304,14 +304,16 @@ export const batchFetchData = async (cloudId, parentAri, blockNodeIdentifiers, c
|
|
|
304
304
|
|
|
305
305
|
// If batch request fails, return error for all resourceIds. Capture the HTTP
|
|
306
306
|
// status from `BlockError` so fetch analytics can break failures down by
|
|
307
|
-
// statusCode (EDITOR-7862); undefined for non-HTTP failures.
|
|
307
|
+
// statusCode (EDITOR-7862); undefined for non-HTTP failures. Thread the PII-safe
|
|
308
|
+
// original message/name so the renderer can de-opaque the `errored` bucket.
|
|
308
309
|
return blockNodeIdentifiers.map(blockNodeIdentifier => ({
|
|
309
310
|
error: {
|
|
310
311
|
type: error instanceof BlockError ? mapBlockError(error) : SyncBlockError.Errored,
|
|
311
312
|
reason: error.message,
|
|
312
313
|
...(error instanceof BlockError && {
|
|
313
314
|
statusCode: error.status
|
|
314
|
-
})
|
|
315
|
+
}),
|
|
316
|
+
...getPiiSafeOriginalError(error)
|
|
315
317
|
},
|
|
316
318
|
resourceId: blockNodeIdentifier.resourceId
|
|
317
319
|
}));
|
|
@@ -466,12 +468,14 @@ class BlockServiceADFFetchProvider {
|
|
|
466
468
|
} catch (error) {
|
|
467
469
|
if (error instanceof BlockError) {
|
|
468
470
|
// Capture the HTTP status so fetch analytics can break failures down by
|
|
469
|
-
// statusCode (EDITOR-7862).
|
|
471
|
+
// statusCode (EDITOR-7862). Thread the PII-safe original message/name so
|
|
472
|
+
// the renderer can de-opaque failures.
|
|
470
473
|
return {
|
|
471
474
|
error: {
|
|
472
475
|
type: mapBlockError(error),
|
|
473
476
|
reason: error.message,
|
|
474
|
-
statusCode: error.status
|
|
477
|
+
statusCode: error.status,
|
|
478
|
+
...getPiiSafeOriginalError(error)
|
|
475
479
|
},
|
|
476
480
|
resourceId
|
|
477
481
|
};
|
|
@@ -479,7 +483,8 @@ class BlockServiceADFFetchProvider {
|
|
|
479
483
|
return {
|
|
480
484
|
error: {
|
|
481
485
|
type: SyncBlockError.Errored,
|
|
482
|
-
reason: error.message
|
|
486
|
+
reason: error.message,
|
|
487
|
+
...getPiiSafeOriginalError(error)
|
|
483
488
|
},
|
|
484
489
|
resourceId
|
|
485
490
|
};
|
|
@@ -8,6 +8,27 @@ export const stringifyError = error => {
|
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* PII-safe extraction of `message`/`name` from an unknown caught error, used to
|
|
13
|
+
* de-opaque `errored` failures. Only ever reads `Error.message` / `Error.name` — it
|
|
14
|
+
* never stringifies arbitrary objects (which could pull in node content / UGC), and
|
|
15
|
+
* non-`Error` values yield `{}` so no `[object Object]` or serialized payload leaks
|
|
16
|
+
* into analytics.
|
|
17
|
+
*/
|
|
18
|
+
export const getPiiSafeOriginalError = error => {
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
return {
|
|
21
|
+
...(error.message && {
|
|
22
|
+
originalMessage: error.message
|
|
23
|
+
}),
|
|
24
|
+
...(error.name && {
|
|
25
|
+
originalName: error.name
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
};
|
|
31
|
+
|
|
11
32
|
/**
|
|
12
33
|
* The set of categorical failure reasons emitted on synced-block operational error
|
|
13
34
|
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
@@ -80,13 +101,16 @@ export const buildErrorAttribution = (gateEnabled, error, statusCode) => {
|
|
|
80
101
|
export const FETCH_BENIGN_REASONS = new Set(['source_deleted', 'source_unpublished', 'source_unsynced', 'source_not_found', 'permission_denied', 'unauthenticated',
|
|
81
102
|
// Mirror the benign write-path enum equivalents so already-classified errors are
|
|
82
103
|
// treated consistently.
|
|
83
|
-
'not_found', 'forbidden', 'unpublished'
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
//
|
|
104
|
+
'not_found', 'forbidden', 'unpublished',
|
|
105
|
+
// `offline` is an expected client connectivity state, not a read-path system failure:
|
|
106
|
+
// the error component is rendered for the offline experience, so it should not inflate
|
|
107
|
+
// the true error rate. It still emits `reason` (so its volume can be inspected) but is
|
|
108
|
+
// excluded from true-error counts.
|
|
109
|
+
'offline'
|
|
110
|
+
// NOTE: `entity_not_found` is intentionally NOT benign — it is retried up to
|
|
111
|
+
// ENTITY_NOT_FOUND_MAX_RETRIES (analytics are suppressed during retries); by the time
|
|
112
|
+
// the error event fires, retries are exhausted and it is a genuine failure. It still
|
|
113
|
+
// emits `reason` (so it can be inspected) but is counted as a true error.
|
|
90
114
|
]);
|
|
91
115
|
|
|
92
116
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/editor/no-re-export */
|
|
2
|
-
export { fetchErrorPayload } from '../utils/errorHandling';
|
|
2
|
+
export { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
|
|
@@ -9,7 +9,7 @@ import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
|
9
9
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
10
10
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
11
11
|
import { isProviderNotReadyError, SyncBlockError } from '../common/types';
|
|
12
|
-
import { buildFetchErrorAttribution, fetchErrorPayload } from '../utils/errorHandling';
|
|
12
|
+
import { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError } from '../utils/errorHandling';
|
|
13
13
|
import { createSyncBlockNode, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
14
14
|
export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resourceId, localId, fireAnalyticsEvent) {
|
|
15
15
|
var _manager$referenceMan2, _manager$referenceMan3, _manager$referenceMan4;
|
|
@@ -100,13 +100,16 @@ export var useFetchSyncBlockData = function useFetchSyncBlockData(manager, resou
|
|
|
100
100
|
});
|
|
101
101
|
fireAnalyticsEvent === null || fireAnalyticsEvent === void 0 || fireAnalyticsEvent(fetchErrorPayload(_t.message, resourceId, getSourceProductFromResourceIdSafe(resourceId), buildFetchErrorAttribution(fg('platform_editor_blocks_patch_3'), _t.message)));
|
|
102
102
|
|
|
103
|
-
//
|
|
103
|
+
// Thread the PII-safe original message/name so the renderer can de-opaque
|
|
104
|
+
// this otherwise-bare `errored` failure. `originalMessage` carries the
|
|
105
|
+
// Error.message and is preferred over `reason` by the renderer, so no
|
|
106
|
+
// separate `reason` is set here.
|
|
104
107
|
setFetchState({
|
|
105
108
|
syncBlockInstance: {
|
|
106
109
|
resourceId: resourceId || '',
|
|
107
|
-
error: {
|
|
110
|
+
error: _objectSpread({
|
|
108
111
|
type: SyncBlockError.Errored
|
|
109
|
-
}
|
|
112
|
+
}, getPiiSafeOriginalError(_t))
|
|
110
113
|
},
|
|
111
114
|
isLoading: false
|
|
112
115
|
});
|
|
@@ -15,7 +15,7 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
|
15
15
|
import { generateBlockAri as _generateBlockAri, generateBlockAriFromReference } from '../../clients/block-service/ari';
|
|
16
16
|
import { batchRetrieveSyncedBlocks, BlockError, BlockNotFoundError, BlockTimeoutError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getReferenceSyncedBlocksByBlockAri, getSyncedBlockContent, updateReferenceSyncedBlockOnDocument, updateSyncedBlock, updateSyncedBlocks } from '../../clients/block-service/blockService';
|
|
17
17
|
import { SyncBlockError } from '../../common/types';
|
|
18
|
-
import { stringifyError } from '../../utils/errorHandling';
|
|
18
|
+
import { getPiiSafeOriginalError, stringifyError } from '../../utils/errorHandling';
|
|
19
19
|
import { createResourceIdForReference } from '../../utils/resourceId';
|
|
20
20
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
21
21
|
var BLOCK_ARI_TO_RESOURCE_ID_REGEX = /^ari:cloud:blocks:.*:synced-block\/(.+)$/;
|
|
@@ -407,12 +407,12 @@ var _batchFetchData = /*#__PURE__*/function () {
|
|
|
407
407
|
case 21:
|
|
408
408
|
return _context2.abrupt("return", blockNodeIdentifiers.map(function (blockNodeIdentifier) {
|
|
409
409
|
return {
|
|
410
|
-
error: _objectSpread({
|
|
410
|
+
error: _objectSpread(_objectSpread({
|
|
411
411
|
type: _t4 instanceof BlockError ? mapBlockError(_t4) : SyncBlockError.Errored,
|
|
412
412
|
reason: _t4.message
|
|
413
413
|
}, _t4 instanceof BlockError && {
|
|
414
414
|
statusCode: _t4.status
|
|
415
|
-
}),
|
|
415
|
+
}), getPiiSafeOriginalError(_t4)),
|
|
416
416
|
resourceId: blockNodeIdentifier.resourceId
|
|
417
417
|
};
|
|
418
418
|
}));
|
|
@@ -663,19 +663,19 @@ var BlockServiceADFFetchProvider = /*#__PURE__*/function () {
|
|
|
663
663
|
break;
|
|
664
664
|
}
|
|
665
665
|
return _context5.abrupt("return", {
|
|
666
|
-
error: {
|
|
666
|
+
error: _objectSpread({
|
|
667
667
|
type: mapBlockError(_t7),
|
|
668
668
|
reason: _t7.message,
|
|
669
669
|
statusCode: _t7.status
|
|
670
|
-
},
|
|
670
|
+
}, getPiiSafeOriginalError(_t7)),
|
|
671
671
|
resourceId: resourceId
|
|
672
672
|
});
|
|
673
673
|
case 5:
|
|
674
674
|
return _context5.abrupt("return", {
|
|
675
|
-
error: {
|
|
675
|
+
error: _objectSpread({
|
|
676
676
|
type: SyncBlockError.Errored,
|
|
677
677
|
reason: _t7.message
|
|
678
|
-
},
|
|
678
|
+
}, getPiiSafeOriginalError(_t7)),
|
|
679
679
|
resourceId: resourceId
|
|
680
680
|
});
|
|
681
681
|
case 6:
|
|
@@ -12,6 +12,24 @@ export var stringifyError = function stringifyError(error) {
|
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* PII-safe extraction of `message`/`name` from an unknown caught error, used to
|
|
17
|
+
* de-opaque `errored` failures. Only ever reads `Error.message` / `Error.name` — it
|
|
18
|
+
* never stringifies arbitrary objects (which could pull in node content / UGC), and
|
|
19
|
+
* non-`Error` values yield `{}` so no `[object Object]` or serialized payload leaks
|
|
20
|
+
* into analytics.
|
|
21
|
+
*/
|
|
22
|
+
export var getPiiSafeOriginalError = function getPiiSafeOriginalError(error) {
|
|
23
|
+
if (error instanceof Error) {
|
|
24
|
+
return _objectSpread(_objectSpread({}, error.message && {
|
|
25
|
+
originalMessage: error.message
|
|
26
|
+
}), error.name && {
|
|
27
|
+
originalName: error.name
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
};
|
|
32
|
+
|
|
15
33
|
/**
|
|
16
34
|
* The set of categorical failure reasons emitted on synced-block operational error
|
|
17
35
|
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
|
@@ -83,13 +101,16 @@ export var buildErrorAttribution = function buildErrorAttribution(gateEnabled, e
|
|
|
83
101
|
export var FETCH_BENIGN_REASONS = new Set(['source_deleted', 'source_unpublished', 'source_unsynced', 'source_not_found', 'permission_denied', 'unauthenticated',
|
|
84
102
|
// Mirror the benign write-path enum equivalents so already-classified errors are
|
|
85
103
|
// treated consistently.
|
|
86
|
-
'not_found', 'forbidden', 'unpublished'
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
//
|
|
104
|
+
'not_found', 'forbidden', 'unpublished',
|
|
105
|
+
// `offline` is an expected client connectivity state, not a read-path system failure:
|
|
106
|
+
// the error component is rendered for the offline experience, so it should not inflate
|
|
107
|
+
// the true error rate. It still emits `reason` (so its volume can be inspected) but is
|
|
108
|
+
// excluded from true-error counts.
|
|
109
|
+
'offline'
|
|
110
|
+
// NOTE: `entity_not_found` is intentionally NOT benign — it is retried up to
|
|
111
|
+
// ENTITY_NOT_FOUND_MAX_RETRIES (analytics are suppressed during retries); by the time
|
|
112
|
+
// the error event fires, retries are exhausted and it is a genuine failure. It still
|
|
113
|
+
// emits `reason` (so it can be inspected) but is counted as a true error.
|
|
93
114
|
]);
|
|
94
115
|
|
|
95
116
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { fetchErrorPayload } from '../utils/errorHandling';
|
|
1
|
+
export { buildFetchErrorAttribution, fetchErrorPayload, getPiiSafeOriginalError, } from '../utils/errorHandling';
|
|
@@ -5,6 +5,17 @@ import { NodeDataProvider } from '@atlaskit/node-data-provider';
|
|
|
5
5
|
import type { TaskDecisionProvider } from '@atlaskit/task-decision/types';
|
|
6
6
|
import type { SyncBlockData, SyncBlockStatus, ResourceId, SyncBlockError, SyncBlockNode, SyncBlockProduct, BlockInstanceId, SyncBlockAttrs, ReferenceSyncBlockData, DeletionReason } from '../common/types';
|
|
7
7
|
type SyncBlockErrorInfo = {
|
|
8
|
+
/**
|
|
9
|
+
* PII-safe `Error.name` from the upstream catch, used to de-opaque `errored`
|
|
10
|
+
* failures. Only ever `Error.name` — never node content, titles, or any UGC.
|
|
11
|
+
*/
|
|
12
|
+
originalName?: string;
|
|
13
|
+
/**
|
|
14
|
+
* PII-safe `Error.message` from the upstream catch, so the classifier can bucket
|
|
15
|
+
* the real cause. Distinct from `reason` (may be synthetic/enum): carries the raw
|
|
16
|
+
* framework/HTTP text. Only ever `Error.message` — never node content or UGC.
|
|
17
|
+
*/
|
|
18
|
+
originalMessage?: string;
|
|
8
19
|
reason?: string;
|
|
9
20
|
sourceAri?: string;
|
|
10
21
|
/**
|
|
@@ -3,6 +3,17 @@ import type { RendererSyncBlockEventPayload, OperationalAEP, SyncBlockEventPaylo
|
|
|
3
3
|
import { SyncBlockError } from '../common/types';
|
|
4
4
|
import type { DeletionMechanism, DeletionReason } from '../common/types';
|
|
5
5
|
export declare const stringifyError: (error: unknown) => string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* PII-safe extraction of `message`/`name` from an unknown caught error, used to
|
|
8
|
+
* de-opaque `errored` failures. Only ever reads `Error.message` / `Error.name` — it
|
|
9
|
+
* never stringifies arbitrary objects (which could pull in node content / UGC), and
|
|
10
|
+
* non-`Error` values yield `{}` so no `[object Object]` or serialized payload leaks
|
|
11
|
+
* into analytics.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getPiiSafeOriginalError: (error: unknown) => {
|
|
14
|
+
originalMessage?: string;
|
|
15
|
+
originalName?: string;
|
|
16
|
+
};
|
|
6
17
|
/**
|
|
7
18
|
* The set of categorical failure reasons emitted on synced-block operational error
|
|
8
19
|
* events (EDITOR-7796). These let the analytics dashboard break delete/update/create
|
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": "^123.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.24.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.5.
|
|
77
|
+
"version": "8.5.6",
|
|
78
78
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
79
79
|
"author": "Atlassian Pty Ltd",
|
|
80
80
|
"license": "Apache-2.0",
|