@atlaskit/editor-synced-block-provider 8.5.3 → 8.5.5
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/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/store-manager/sourceSyncBlockStoreManager.js +1 -1
- package/dist/cjs/utils/errorHandling.js +31 -10
- 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/store-manager/sourceSyncBlockStoreManager.js +2 -2
- package/dist/es2019/utils/errorHandling.js +32 -8
- 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/store-manager/sourceSyncBlockStoreManager.js +2 -2
- package/dist/esm/utils/errorHandling.js +29 -8
- 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 +12 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 8.5.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`b1ff8c881a4aa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b1ff8c881a4aa) -
|
|
8
|
+
[ux] Synced Blocks: de-opaque the renderer error-boundary fetch analytics event. The event now
|
|
9
|
+
routes through the shared fetch-error attribution builder (emitting structured
|
|
10
|
+
`reason`/`benign`/`sourceProduct`/`statusCode`, gated on `platform_editor_blocks_patch_4`) and a
|
|
11
|
+
PII-safe original error message is threaded from the upstream fetch catch so previously-opaque
|
|
12
|
+
`errored` failures can be classified and inspected.
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
|
|
15
|
+
## 8.5.4
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`1b4390dfedd97`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1b4390dfedd97) -
|
|
20
|
+
Rename the internal analytics payload builder `createSuccessPayloadNew` to
|
|
21
|
+
`createSuccessOperationalPayload` to make the operational-vs-track distinction clear at the call
|
|
22
|
+
site. This is a pure rename with no behavioural or payload change.
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
|
|
3
25
|
## 8.5.3
|
|
4
26
|
|
|
5
27
|
### 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:
|
|
@@ -414,7 +414,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
414
414
|
// Operational create-success event with the join key.
|
|
415
415
|
if ((0, _platformFeatureFlags.fg)('platform_editor_blocks_patch_4')) {
|
|
416
416
|
var _this$fireAnalyticsEv5, _this$syncBlockCache$;
|
|
417
|
-
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, (0, _errorHandling.
|
|
417
|
+
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, (0, _errorHandling.createSuccessOperationalPayload)(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
418
418
|
}
|
|
419
419
|
} else {
|
|
420
420
|
var _this$fireAnalyticsEv6;
|
|
@@ -4,9 +4,9 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.fetchSuccessPayload = exports.fetchReferencesErrorPayload = exports.fetchErrorPayload = exports.deleteSuccessPayload = exports.deleteErrorPayload = exports.
|
|
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
|
/**
|
|
@@ -340,7 +361,7 @@ var createSuccessPayload = exports.createSuccessPayload = function createSuccess
|
|
|
340
361
|
* existed, so dashboards had no create denominator). Fired behind
|
|
341
362
|
* `platform_editor_blocks_patch_4` with the `blockInstanceId` join key.
|
|
342
363
|
*/
|
|
343
|
-
var
|
|
364
|
+
var createSuccessOperationalPayload = exports.createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct) {
|
|
344
365
|
return {
|
|
345
366
|
action: _analytics.ACTION.INSERTED,
|
|
346
367
|
actionSubject: _analytics.ACTION_SUBJECT.SYNCED_BLOCK,
|
|
@@ -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
|
};
|
|
@@ -2,7 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
3
3
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
4
|
import { SyncBlockError } from '../common/types';
|
|
5
|
-
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload,
|
|
5
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
6
6
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
7
7
|
import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
8
8
|
/** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
|
|
@@ -341,7 +341,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
341
341
|
// Operational create-success event with the join key.
|
|
342
342
|
if (fg('platform_editor_blocks_patch_4')) {
|
|
343
343
|
var _this$fireAnalyticsEv7, _this$syncBlockCache$;
|
|
344
|
-
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this,
|
|
344
|
+
(_this$fireAnalyticsEv7 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv7 === void 0 ? void 0 : _this$fireAnalyticsEv7.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
345
345
|
}
|
|
346
346
|
} else {
|
|
347
347
|
var _this$fireAnalyticsEv8;
|
|
@@ -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
|
/**
|
|
@@ -319,7 +343,7 @@ export const createSuccessPayload = (resourceId, sourceProduct) => {
|
|
|
319
343
|
* existed, so dashboards had no create denominator). Fired behind
|
|
320
344
|
* `platform_editor_blocks_patch_4` with the `blockInstanceId` join key.
|
|
321
345
|
*/
|
|
322
|
-
export const
|
|
346
|
+
export const createSuccessOperationalPayload = (resourceId, blockInstanceId, sourceProduct) => ({
|
|
323
347
|
action: ACTION.INSERTED,
|
|
324
348
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
325
349
|
actionSubjectId: ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE,
|
|
@@ -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,7 +12,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
12
12
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
13
13
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
14
14
|
import { SyncBlockError } from '../common/types';
|
|
15
|
-
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload,
|
|
15
|
+
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, createSuccessOperationalPayload, deleteSuccessPayload, fetchReferencesErrorPayload, buildErrorAttribution } from '../utils/errorHandling';
|
|
16
16
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
17
17
|
import { convertSyncBlockPMNodeToSyncBlockData, getSourceProductFromResourceIdSafe } from '../utils/utils';
|
|
18
18
|
/** Maximum time (ms) flush() will wait for in-flight block creations before proceeding. */
|
|
@@ -407,7 +407,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
407
407
|
// Operational create-success event with the join key.
|
|
408
408
|
if (fg('platform_editor_blocks_patch_4')) {
|
|
409
409
|
var _this$fireAnalyticsEv5, _this$syncBlockCache$;
|
|
410
|
-
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this,
|
|
410
|
+
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 || _this$fireAnalyticsEv5.call(this, createSuccessOperationalPayload(resourceId || '', (_this$syncBlockCache$ = this.syncBlockCache.get(resourceId)) === null || _this$syncBlockCache$ === void 0 ? void 0 : _this$syncBlockCache$.blockInstanceId, sourceProduct));
|
|
411
411
|
}
|
|
412
412
|
} else {
|
|
413
413
|
var _this$fireAnalyticsEv6;
|
|
@@ -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
|
/**
|
|
@@ -331,7 +352,7 @@ export var createSuccessPayload = function createSuccessPayload(resourceId, sour
|
|
|
331
352
|
* existed, so dashboards had no create denominator). Fired behind
|
|
332
353
|
* `platform_editor_blocks_patch_4` with the `blockInstanceId` join key.
|
|
333
354
|
*/
|
|
334
|
-
export var
|
|
355
|
+
export var createSuccessOperationalPayload = function createSuccessOperationalPayload(resourceId, blockInstanceId, sourceProduct) {
|
|
335
356
|
return {
|
|
336
357
|
action: ACTION.INSERTED,
|
|
337
358
|
actionSubject: ACTION_SUBJECT.SYNCED_BLOCK,
|
|
@@ -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
|
|
@@ -168,7 +179,7 @@ export declare const createSuccessPayload: (resourceId: string, sourceProduct?:
|
|
|
168
179
|
* existed, so dashboards had no create denominator). Fired behind
|
|
169
180
|
* `platform_editor_blocks_patch_4` with the `blockInstanceId` join key.
|
|
170
181
|
*/
|
|
171
|
-
export declare const
|
|
182
|
+
export declare const createSuccessOperationalPayload: (resourceId: string, blockInstanceId?: string, sourceProduct?: string) => SyncBlockEventPayload;
|
|
172
183
|
export declare const updateSuccessPayload: (resourceId: string, hasReference?: boolean, sourceProduct?: string) => SyncBlockEventPayload;
|
|
173
184
|
/**
|
|
174
185
|
* Optional enrichment for the `syncedBlockDelete` success event behind
|
package/package.json
CHANGED
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
],
|
|
17
17
|
"atlaskit:src": "src/index.ts",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@atlaskit/adf-utils": "^20.
|
|
19
|
+
"@atlaskit/adf-utils": "^20.3.0",
|
|
20
20
|
"@atlaskit/editor-json-transformer": "^9.0.0",
|
|
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": "^122.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.5",
|
|
78
78
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
79
79
|
"author": "Atlassian Pty Ltd",
|
|
80
80
|
"license": "Apache-2.0",
|