@atlaskit/editor-synced-block-provider 4.0.1 → 4.1.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 +19 -0
- package/dist/cjs/clients/block-service/blockService.js +35 -17
- package/dist/cjs/common/types.js +2 -0
- package/dist/cjs/providers/block-service/blockServiceAPI.js +16 -1
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +209 -811
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +3 -6
- package/dist/cjs/store-manager/syncBlockSubscriptionManager.js +180 -17
- package/dist/es2019/clients/block-service/blockService.js +16 -3
- package/dist/es2019/common/types.js +2 -0
- package/dist/es2019/providers/block-service/blockServiceAPI.js +12 -1
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +78 -595
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +3 -6
- package/dist/es2019/store-manager/syncBlockSubscriptionManager.js +157 -10
- package/dist/esm/clients/block-service/blockService.js +34 -16
- package/dist/esm/common/types.js +2 -0
- package/dist/esm/providers/block-service/blockServiceAPI.js +17 -2
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +210 -812
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +3 -6
- package/dist/esm/store-manager/syncBlockSubscriptionManager.js +180 -17
- package/dist/types/clients/block-service/blockService.d.ts +4 -1
- package/dist/types/common/types.d.ts +2 -1
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +4 -49
- package/dist/types/store-manager/syncBlockSubscriptionManager.d.ts +25 -6
- package/dist/types-ts4.5/clients/block-service/blockService.d.ts +4 -1
- package/dist/types-ts4.5/common/types.d.ts +2 -1
- package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +4 -49
- package/dist/types-ts4.5/store-manager/syncBlockSubscriptionManager.d.ts +25 -6
- package/package.json +4 -7
|
@@ -12,7 +12,6 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
|
|
|
12
12
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
13
|
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
14
14
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
15
|
-
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
16
15
|
var _types = require("../common/types");
|
|
17
16
|
var _errorHandling = require("../utils/errorHandling");
|
|
18
17
|
var _experienceTracking = require("../utils/experienceTracking");
|
|
@@ -72,11 +71,9 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
72
71
|
throw new Error('Local ID or resource ID is not set');
|
|
73
72
|
}
|
|
74
73
|
var syncBlockData = (0, _utils.convertSyncBlockPMNodeToSyncBlockData)(syncBlockNode);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.hasReceivedContentChange = true;
|
|
79
|
-
}
|
|
74
|
+
var cachedBlock = this.syncBlockCache.get(resourceId);
|
|
75
|
+
if (cachedBlock && !(0, _isEqual.default)(syncBlockData.content, cachedBlock.content)) {
|
|
76
|
+
this.hasReceivedContentChange = true;
|
|
80
77
|
}
|
|
81
78
|
this.syncBlockCache.set(resourceId, _objectSpread(_objectSpread({}, syncBlockData), {}, {
|
|
82
79
|
isDirty: true
|
|
@@ -9,26 +9,45 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
|
|
|
9
9
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
10
10
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
11
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
12
|
-
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
12
|
var _errorHandling = require("../utils/errorHandling");
|
|
14
13
|
var _resolveSyncBlockInstance = require("../utils/resolveSyncBlockInstance");
|
|
15
14
|
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; } } }; }
|
|
16
15
|
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; } }
|
|
17
16
|
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; }
|
|
17
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
18
|
+
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; }
|
|
18
19
|
/**
|
|
19
20
|
* Manages the lifecycle of GraphQL WebSocket subscriptions for sync block
|
|
20
|
-
* real-time updates,
|
|
21
|
-
*
|
|
21
|
+
* real-time updates, owns the subscriptions and titleSubscriptions maps,
|
|
22
|
+
* and provides a listener API so React components can react when the set
|
|
23
|
+
* of subscribed resource IDs changes.
|
|
22
24
|
*/
|
|
23
25
|
var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__PURE__*/function () {
|
|
24
26
|
function SyncBlockSubscriptionManager(deps) {
|
|
25
27
|
(0, _classCallCheck2.default)(this, SyncBlockSubscriptionManager);
|
|
28
|
+
(0, _defineProperty2.default)(this, "subscriptions", new Map());
|
|
29
|
+
(0, _defineProperty2.default)(this, "titleSubscriptions", new Map());
|
|
26
30
|
(0, _defineProperty2.default)(this, "graphqlSubscriptions", new Map());
|
|
27
31
|
(0, _defineProperty2.default)(this, "subscriptionChangeListeners", new Set());
|
|
28
32
|
(0, _defineProperty2.default)(this, "useRealTimeSubscriptions", false);
|
|
33
|
+
// Track pending cache deletions to handle block moves (unmount/remount)
|
|
34
|
+
// When a block is moved, the old component unmounts before the new one mounts,
|
|
35
|
+
// causing the cache to be deleted prematurely. We delay deletion to allow
|
|
36
|
+
// the new component to subscribe and cancel the pending deletion.
|
|
37
|
+
(0, _defineProperty2.default)(this, "pendingCacheDeletions", new Map());
|
|
29
38
|
this.deps = deps;
|
|
30
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the subscriptions map. Used by external consumers (e.g. batch fetcher, flush)
|
|
43
|
+
* that need to read the current subscription state.
|
|
44
|
+
*/
|
|
31
45
|
return (0, _createClass2.default)(SyncBlockSubscriptionManager, [{
|
|
46
|
+
key: "getSubscriptions",
|
|
47
|
+
value: function getSubscriptions() {
|
|
48
|
+
return this.subscriptions;
|
|
49
|
+
}
|
|
50
|
+
}, {
|
|
32
51
|
key: "setRealTimeSubscriptionsEnabled",
|
|
33
52
|
value: function setRealTimeSubscriptionsEnabled(enabled) {
|
|
34
53
|
if (this.useRealTimeSubscriptions === enabled) {
|
|
@@ -49,7 +68,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
49
68
|
}, {
|
|
50
69
|
key: "getSubscribedResourceIds",
|
|
51
70
|
value: function getSubscribedResourceIds() {
|
|
52
|
-
return Array.from(this.
|
|
71
|
+
return Array.from(this.subscriptions.keys());
|
|
53
72
|
}
|
|
54
73
|
}, {
|
|
55
74
|
key: "onSubscriptionsChanged",
|
|
@@ -89,10 +108,140 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
89
108
|
this.deps.fetchSyncBlockSourceInfo(resolved.resourceId);
|
|
90
109
|
}
|
|
91
110
|
}
|
|
111
|
+
}, {
|
|
112
|
+
key: "subscribeToSyncBlock",
|
|
113
|
+
value: function subscribeToSyncBlock(resourceId, localId, callback) {
|
|
114
|
+
var _this3 = this;
|
|
115
|
+
// Cancel any pending cache deletion for this resourceId.
|
|
116
|
+
// This handles the case where a block is moved - the old component unmounts
|
|
117
|
+
// (scheduling deletion) but the new component mounts and subscribes before
|
|
118
|
+
// the deletion timeout fires.
|
|
119
|
+
var pendingDeletion = this.pendingCacheDeletions.get(resourceId);
|
|
120
|
+
if (pendingDeletion) {
|
|
121
|
+
clearTimeout(pendingDeletion);
|
|
122
|
+
this.pendingCacheDeletions.delete(resourceId);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// add to subscriptions map
|
|
126
|
+
var resourceSubscriptions = this.subscriptions.get(resourceId) || {};
|
|
127
|
+
var isNewResourceSubscription = Object.keys(resourceSubscriptions).length === 0;
|
|
128
|
+
this.subscriptions.set(resourceId, _objectSpread(_objectSpread({}, resourceSubscriptions), {}, (0, _defineProperty2.default)({}, localId, callback)));
|
|
129
|
+
|
|
130
|
+
// New subscription means new reference synced block is added to the document
|
|
131
|
+
this.deps.markCacheDirty();
|
|
132
|
+
|
|
133
|
+
// Notify listeners if this is a new resource subscription
|
|
134
|
+
if (isNewResourceSubscription) {
|
|
135
|
+
this.notifySubscriptionChangeListeners();
|
|
136
|
+
}
|
|
137
|
+
var cachedData = this.deps.getFromCache(resourceId);
|
|
138
|
+
if (cachedData) {
|
|
139
|
+
callback(cachedData);
|
|
140
|
+
} else {
|
|
141
|
+
this.deps.debouncedBatchedFetchSyncBlocks(resourceId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Set up GraphQL subscription if real-time subscriptions are enabled
|
|
145
|
+
if (this.shouldUseRealTime()) {
|
|
146
|
+
this.setupSubscription(resourceId);
|
|
147
|
+
}
|
|
148
|
+
return function () {
|
|
149
|
+
var resourceSubscriptions = _this3.subscriptions.get(resourceId);
|
|
150
|
+
if (resourceSubscriptions) {
|
|
151
|
+
// Unsubscription means a reference synced block is removed from the document
|
|
152
|
+
_this3.deps.markCacheDirty();
|
|
153
|
+
delete resourceSubscriptions[localId];
|
|
154
|
+
if (Object.keys(resourceSubscriptions).length === 0) {
|
|
155
|
+
_this3.subscriptions.delete(resourceId);
|
|
156
|
+
|
|
157
|
+
// Clean up GraphQL subscription when no more local subscribers
|
|
158
|
+
_this3.cleanupSubscription(resourceId);
|
|
159
|
+
|
|
160
|
+
// Notify listeners that subscription was removed
|
|
161
|
+
_this3.notifySubscriptionChangeListeners();
|
|
162
|
+
|
|
163
|
+
// Delay cache deletion to handle block moves (unmount/remount).
|
|
164
|
+
// When a block is moved, the old component unmounts before the new one mounts.
|
|
165
|
+
// By delaying deletion, we give the new component time to subscribe and
|
|
166
|
+
// cancel this pending deletion, preserving the cached data.
|
|
167
|
+
// TODO: EDITOR-4152 - Rework this logic
|
|
168
|
+
var deletionTimeout = setTimeout(function () {
|
|
169
|
+
// Only delete if still no subscribers (wasn't re-subscribed)
|
|
170
|
+
if (!_this3.subscriptions.has(resourceId)) {
|
|
171
|
+
_this3.deps.deleteFromCache(resourceId);
|
|
172
|
+
}
|
|
173
|
+
_this3.pendingCacheDeletions.delete(resourceId);
|
|
174
|
+
}, 1000);
|
|
175
|
+
_this3.pendingCacheDeletions.set(resourceId, deletionTimeout);
|
|
176
|
+
} else {
|
|
177
|
+
_this3.subscriptions.set(resourceId, resourceSubscriptions);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}, {
|
|
183
|
+
key: "subscribeToSourceTitle",
|
|
184
|
+
value: function subscribeToSourceTitle(node, callback) {
|
|
185
|
+
var _cachedData$data,
|
|
186
|
+
_this4 = this;
|
|
187
|
+
// check node is a sync block, as we only support sync block subscriptions
|
|
188
|
+
if (node.type.name !== 'syncBlock') {
|
|
189
|
+
return function () {};
|
|
190
|
+
}
|
|
191
|
+
var _node$attrs = node.attrs,
|
|
192
|
+
resourceId = _node$attrs.resourceId,
|
|
193
|
+
localId = _node$attrs.localId;
|
|
194
|
+
if (!localId || !resourceId) {
|
|
195
|
+
return function () {};
|
|
196
|
+
}
|
|
197
|
+
var cachedData = this.deps.getFromCache(resourceId);
|
|
198
|
+
if (cachedData !== null && cachedData !== void 0 && (_cachedData$data = cachedData.data) !== null && _cachedData$data !== void 0 && _cachedData$data.sourceTitle) {
|
|
199
|
+
callback(cachedData.data.sourceTitle);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// add to subscriptions map
|
|
203
|
+
var resourceSubscriptions = this.titleSubscriptions.get(resourceId) || {};
|
|
204
|
+
this.titleSubscriptions.set(resourceId, _objectSpread(_objectSpread({}, resourceSubscriptions), {}, (0, _defineProperty2.default)({}, localId, callback)));
|
|
205
|
+
return function () {
|
|
206
|
+
var resourceSubscriptions = _this4.titleSubscriptions.get(resourceId);
|
|
207
|
+
if (resourceSubscriptions) {
|
|
208
|
+
delete resourceSubscriptions[localId];
|
|
209
|
+
if (Object.keys(resourceSubscriptions).length === 0) {
|
|
210
|
+
_this4.titleSubscriptions.delete(resourceId);
|
|
211
|
+
} else {
|
|
212
|
+
_this4.titleSubscriptions.set(resourceId, resourceSubscriptions);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}, {
|
|
218
|
+
key: "updateSourceTitleSubscriptions",
|
|
219
|
+
value: function updateSourceTitleSubscriptions(resourceId, title) {
|
|
220
|
+
var callbacks = this.titleSubscriptions.get(resourceId);
|
|
221
|
+
if (callbacks) {
|
|
222
|
+
Object.values(callbacks).forEach(function (callback) {
|
|
223
|
+
callback(title);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Notifies all subscription callbacks for a given resource ID with the provided sync block instance.
|
|
230
|
+
*/
|
|
231
|
+
}, {
|
|
232
|
+
key: "notifySubscriptionCallbacks",
|
|
233
|
+
value: function notifySubscriptionCallbacks(resourceId, syncBlock) {
|
|
234
|
+
var callbacks = this.subscriptions.get(resourceId);
|
|
235
|
+
if (callbacks) {
|
|
236
|
+
Object.values(callbacks).forEach(function (callback) {
|
|
237
|
+
callback(syncBlock);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
92
241
|
}, {
|
|
93
242
|
key: "setupSubscription",
|
|
94
243
|
value: function setupSubscription(resourceId) {
|
|
95
|
-
var
|
|
244
|
+
var _this5 = this;
|
|
96
245
|
if (this.graphqlSubscriptions.has(resourceId)) {
|
|
97
246
|
return;
|
|
98
247
|
}
|
|
@@ -101,13 +250,13 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
101
250
|
return;
|
|
102
251
|
}
|
|
103
252
|
var unsubscribe = dataProvider.subscribeToBlockUpdates(resourceId, function (syncBlockInstance) {
|
|
104
|
-
|
|
253
|
+
_this5.handleGraphQLUpdate(syncBlockInstance);
|
|
105
254
|
}, function (error) {
|
|
106
|
-
var
|
|
255
|
+
var _this5$deps$getFireAn;
|
|
107
256
|
(0, _monitoring.logException)(error, {
|
|
108
257
|
location: 'editor-synced-block-provider/syncBlockSubscriptionManager/graphql-subscription'
|
|
109
258
|
});
|
|
110
|
-
(
|
|
259
|
+
(_this5$deps$getFireAn = _this5.deps.getFireAnalyticsEvent()) === null || _this5$deps$getFireAn === void 0 || _this5$deps$getFireAn((0, _errorHandling.fetchErrorPayload)(error.message));
|
|
111
260
|
});
|
|
112
261
|
if (unsubscribe) {
|
|
113
262
|
this.graphqlSubscriptions.set(resourceId, unsubscribe);
|
|
@@ -125,7 +274,7 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
125
274
|
}, {
|
|
126
275
|
key: "setupSubscriptionsForAllBlocks",
|
|
127
276
|
value: function setupSubscriptionsForAllBlocks() {
|
|
128
|
-
var _iterator = _createForOfIteratorHelper(this.
|
|
277
|
+
var _iterator = _createForOfIteratorHelper(this.subscriptions.keys()),
|
|
129
278
|
_step;
|
|
130
279
|
try {
|
|
131
280
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
@@ -159,8 +308,25 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
159
308
|
key: "destroy",
|
|
160
309
|
value: function destroy() {
|
|
161
310
|
this.cleanupAll();
|
|
311
|
+
this.subscriptions.clear();
|
|
312
|
+
this.titleSubscriptions.clear();
|
|
162
313
|
this.subscriptionChangeListeners.clear();
|
|
163
314
|
this.useRealTimeSubscriptions = false;
|
|
315
|
+
|
|
316
|
+
// Clear any pending cache deletions
|
|
317
|
+
var _iterator3 = _createForOfIteratorHelper(this.pendingCacheDeletions.values()),
|
|
318
|
+
_step3;
|
|
319
|
+
try {
|
|
320
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
321
|
+
var timeout = _step3.value;
|
|
322
|
+
clearTimeout(timeout);
|
|
323
|
+
}
|
|
324
|
+
} catch (err) {
|
|
325
|
+
_iterator3.e(err);
|
|
326
|
+
} finally {
|
|
327
|
+
_iterator3.f();
|
|
328
|
+
}
|
|
329
|
+
this.pendingCacheDeletions.clear();
|
|
164
330
|
}
|
|
165
331
|
}, {
|
|
166
332
|
key: "shouldUseRealTime",
|
|
@@ -170,22 +336,19 @@ var SyncBlockSubscriptionManager = exports.SyncBlockSubscriptionManager = /*#__P
|
|
|
170
336
|
}, {
|
|
171
337
|
key: "handleGraphQLUpdate",
|
|
172
338
|
value: function handleGraphQLUpdate(syncBlockInstance) {
|
|
173
|
-
var
|
|
339
|
+
var _this6 = this;
|
|
174
340
|
if (!syncBlockInstance.resourceId) {
|
|
175
|
-
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
throw new Error('Sync block instance provided to graphql subscription update missing resource id');
|
|
341
|
+
return;
|
|
179
342
|
}
|
|
180
343
|
var existing = this.deps.getFromCache(syncBlockInstance.resourceId);
|
|
181
344
|
var resolved = existing ? (0, _resolveSyncBlockInstance.resolveSyncBlockInstance)(existing, syncBlockInstance) : syncBlockInstance;
|
|
182
345
|
this.deps.updateCache(resolved);
|
|
183
346
|
if (!syncBlockInstance.error) {
|
|
184
|
-
var callbacks = this.
|
|
347
|
+
var callbacks = this.subscriptions.get(syncBlockInstance.resourceId);
|
|
185
348
|
var localIds = callbacks ? Object.keys(callbacks) : [];
|
|
186
349
|
localIds.forEach(function (localId) {
|
|
187
|
-
var
|
|
188
|
-
(
|
|
350
|
+
var _this6$deps$getFireAn, _syncBlockInstance$da;
|
|
351
|
+
(_this6$deps$getFireAn = _this6.deps.getFireAnalyticsEvent()) === null || _this6$deps$getFireAn === void 0 || _this6$deps$getFireAn((0, _errorHandling.fetchSuccessPayload)(syncBlockInstance.resourceId, localId, (_syncBlockInstance$da = syncBlockInstance.data) === null || _syncBlockInstance$da === void 0 ? void 0 : _syncBlockInstance$da.product));
|
|
189
352
|
});
|
|
190
353
|
this.deps.fetchSyncBlockSourceInfo(resolved.resourceId);
|
|
191
354
|
} else {
|
|
@@ -297,6 +297,12 @@ export class BlockError extends Error {
|
|
|
297
297
|
this.status = status;
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
+
export class BlockTimeoutError extends Error {
|
|
301
|
+
constructor() {
|
|
302
|
+
super('Block request timed out');
|
|
303
|
+
this.name = 'BlockTimeoutError';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
300
306
|
export const getSyncedBlockContent = async ({
|
|
301
307
|
blockAri
|
|
302
308
|
}) => {
|
|
@@ -334,20 +340,27 @@ export const getSyncedBlockContent = async ({
|
|
|
334
340
|
* @returns A promise containing arrays of successfully fetched blocks and any errors encountered
|
|
335
341
|
*/
|
|
336
342
|
export const batchRetrieveSyncedBlocks = async ({
|
|
337
|
-
blockIdentifiers
|
|
343
|
+
blockIdentifiers,
|
|
344
|
+
config
|
|
338
345
|
}) => {
|
|
339
|
-
var _result$data2;
|
|
346
|
+
var _config$timeoutMs, _result$data2;
|
|
340
347
|
const blockAris = blockIdentifiers.map(blockIdentifier => blockIdentifier.blockAri);
|
|
341
348
|
const bodyData = {
|
|
342
349
|
query: buildBatchRetrieveBlocksQuery(blockAris),
|
|
343
350
|
operationName: BATCH_RETRIEVE_BLOCKS_OPERATION_NAME
|
|
344
351
|
};
|
|
345
352
|
const url = `${GRAPHQL_ENDPOINT}?operation=editorSyncedBlockBatchRetrieveBlocks`;
|
|
346
|
-
|
|
353
|
+
|
|
354
|
+
// undefined or 0 or negative means no timeout,
|
|
355
|
+
// We don't enforce a minimum timeout for simplicity
|
|
356
|
+
|
|
357
|
+
const fetchPromise = fetchWithRetry(url, {
|
|
347
358
|
method: 'POST',
|
|
348
359
|
headers: COMMON_HEADERS,
|
|
349
360
|
body: JSON.stringify(bodyData)
|
|
350
361
|
});
|
|
362
|
+
const timeoutMs = (_config$timeoutMs = config === null || config === void 0 ? void 0 : config.timeoutMs) !== null && _config$timeoutMs !== void 0 ? _config$timeoutMs : 0;
|
|
363
|
+
const response = await (timeoutMs > 0 ? Promise.race([fetchPromise, new Promise((_, reject) => setTimeout(() => reject(new BlockTimeoutError()), timeoutMs))]) : fetchPromise);
|
|
351
364
|
if (!response.ok) {
|
|
352
365
|
throw new BlockError(response.status);
|
|
353
366
|
}
|
|
@@ -11,5 +11,7 @@ export let SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
|
|
|
11
11
|
// content is not a valid JSON
|
|
12
12
|
SyncBlockError["Offline"] = "offline";
|
|
13
13
|
SyncBlockError["Unpublished"] = "unpublished";
|
|
14
|
+
// request was aborted, typically due to client timeout
|
|
15
|
+
SyncBlockError["Aborted"] = "aborted";
|
|
14
16
|
return SyncBlockError;
|
|
15
17
|
}({});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint-disable require-unicode-regexp */
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
3
4
|
import { generateBlockAri, generateBlockAriFromReference } from '../../clients/block-service/ari';
|
|
4
|
-
import { batchRetrieveSyncedBlocks, BlockError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getReferenceSyncedBlocksByBlockAri, getSyncedBlockContent, updateReferenceSyncedBlockOnDocument, updateSyncedBlock, updateSyncedBlocks } from '../../clients/block-service/blockService';
|
|
5
|
+
import { batchRetrieveSyncedBlocks, BlockError, BlockTimeoutError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getReferenceSyncedBlocksByBlockAri, getSyncedBlockContent, updateReferenceSyncedBlockOnDocument, updateSyncedBlock, updateSyncedBlocks } from '../../clients/block-service/blockService';
|
|
5
6
|
import { subscribeToBlockUpdates as subscribeToBlockUpdatesWS } from '../../clients/block-service/blockSubscription';
|
|
6
7
|
import { SyncBlockError } from '../../common/types';
|
|
7
8
|
import { stringifyError } from '../../utils/errorHandling';
|
|
@@ -287,6 +288,16 @@ export const batchFetchData = async (cloudId, parentAri, blockNodeIdentifiers, c
|
|
|
287
288
|
}
|
|
288
289
|
return results;
|
|
289
290
|
} catch (error) {
|
|
291
|
+
if (error instanceof BlockTimeoutError && expValEquals('platform_editor_sync_block_ssr_config', 'isEnabled', true)) {
|
|
292
|
+
return blockNodeIdentifiers.map(blockNodeIdentifier => ({
|
|
293
|
+
error: {
|
|
294
|
+
type: SyncBlockError.Aborted,
|
|
295
|
+
reason: error.message
|
|
296
|
+
},
|
|
297
|
+
resourceId: blockNodeIdentifier.resourceId
|
|
298
|
+
}));
|
|
299
|
+
}
|
|
300
|
+
|
|
290
301
|
// If batch request fails, return error for all resourceIds
|
|
291
302
|
return blockNodeIdentifiers.map(blockNodeIdentifier => ({
|
|
292
303
|
error: {
|