@atlaskit/editor-synced-block-provider 2.15.1 → 2.15.3

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/clients/block-service/blockService.js +42 -1
  3. package/dist/cjs/providers/block-service/blockServiceAPI.js +62 -3
  4. package/dist/cjs/providers/confluence/confluenceContentAPI.js +7 -0
  5. package/dist/cjs/providers/syncBlockProvider.js +5 -0
  6. package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +92 -0
  7. package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +27 -13
  8. package/dist/cjs/utils/errorHandling.js +4 -1
  9. package/dist/es2019/clients/block-service/blockService.js +19 -0
  10. package/dist/es2019/providers/block-service/blockServiceAPI.js +34 -1
  11. package/dist/es2019/providers/confluence/confluenceContentAPI.js +5 -0
  12. package/dist/es2019/providers/syncBlockProvider.js +3 -0
  13. package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +59 -1
  14. package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +18 -4
  15. package/dist/es2019/utils/errorHandling.js +1 -0
  16. package/dist/esm/clients/block-service/blockService.js +41 -0
  17. package/dist/esm/providers/block-service/blockServiceAPI.js +63 -4
  18. package/dist/esm/providers/confluence/confluenceContentAPI.js +7 -0
  19. package/dist/esm/providers/syncBlockProvider.js +5 -0
  20. package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +93 -1
  21. package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +27 -12
  22. package/dist/esm/utils/errorHandling.js +3 -0
  23. package/dist/types/clients/block-service/blockService.d.ts +16 -4
  24. package/dist/types/providers/block-service/blockServiceAPI.d.ts +3 -2
  25. package/dist/types/providers/confluence/confluenceContentAPI.d.ts +3 -2
  26. package/dist/types/providers/syncBlockProvider.d.ts +3 -2
  27. package/dist/types/providers/types.d.ts +13 -1
  28. package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +7 -0
  29. package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +1 -1
  30. package/dist/types/utils/errorHandling.d.ts +1 -0
  31. package/dist/types-ts4.5/clients/block-service/blockService.d.ts +16 -4
  32. package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +3 -2
  33. package/dist/types-ts4.5/providers/confluence/confluenceContentAPI.d.ts +3 -2
  34. package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +3 -2
  35. package/dist/types-ts4.5/providers/types.d.ts +13 -1
  36. package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +7 -0
  37. package/dist/types-ts4.5/store-manager/sourceSyncBlockStoreManager.d.ts +1 -1
  38. package/dist/types-ts4.5/utils/errorHandling.d.ts +1 -0
  39. package/package.json +2 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 2.15.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`f14e76661c943`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f14e76661c943) -
8
+ [EDITOR-2844] Save reference synced block on document to BE when a page is saved
9
+ - Updated dependencies
10
+
11
+ ## 2.15.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`70face9ce7f1b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/70face9ce7f1b) -
16
+ EDITOR-3778 optimise flush to only send to BE when sync block data changes
17
+ - Updated dependencies
18
+
3
19
  ## 2.15.1
4
20
 
5
21
  ### Patch Changes
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.updateSyncedBlock = exports.isBlockContentResponse = exports.getSyncedBlockContent = exports.getReferenceSyncedBlocks = exports.deleteSyncedBlock = exports.createSyncedBlock = exports.BlockError = void 0;
7
+ exports.updateSyncedBlock = exports.updateReferenceSyncedBlockOnDocument = exports.isBlockContentResponse = exports.getSyncedBlockContent = exports.getReferenceSyncedBlocks = exports.deleteSyncedBlock = exports.createSyncedBlock = exports.BlockError = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
10
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -247,4 +247,45 @@ var createSyncedBlock = exports.createSyncedBlock = /*#__PURE__*/function () {
247
247
  return function createSyncedBlock(_x5) {
248
248
  return _ref9.apply(this, arguments);
249
249
  };
250
+ }();
251
+ var updateReferenceSyncedBlockOnDocument = exports.updateReferenceSyncedBlockOnDocument = /*#__PURE__*/function () {
252
+ var _ref1 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(_ref0) {
253
+ var documentAri, blocks, _ref0$noContent, noContent, response;
254
+ return _regenerator.default.wrap(function _callee6$(_context6) {
255
+ while (1) switch (_context6.prev = _context6.next) {
256
+ case 0:
257
+ documentAri = _ref0.documentAri, blocks = _ref0.blocks, _ref0$noContent = _ref0.noContent, noContent = _ref0$noContent === void 0 ? true : _ref0$noContent;
258
+ _context6.next = 3;
259
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/document/").concat(encodeURIComponent(documentAri), "/references?noContent=").concat(noContent), {
260
+ method: 'PUT',
261
+ headers: COMMON_HEADERS,
262
+ body: JSON.stringify({
263
+ blocks: blocks
264
+ })
265
+ });
266
+ case 3:
267
+ response = _context6.sent;
268
+ if (response.ok) {
269
+ _context6.next = 6;
270
+ break;
271
+ }
272
+ throw new BlockError(response.status);
273
+ case 6:
274
+ if (noContent) {
275
+ _context6.next = 10;
276
+ break;
277
+ }
278
+ _context6.next = 9;
279
+ return response.json();
280
+ case 9:
281
+ return _context6.abrupt("return", _context6.sent);
282
+ case 10:
283
+ case "end":
284
+ return _context6.stop();
285
+ }
286
+ }, _callee6);
287
+ }));
288
+ return function updateReferenceSyncedBlockOnDocument(_x6) {
289
+ return _ref1.apply(this, arguments);
290
+ };
250
291
  }();
@@ -325,21 +325,30 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
325
325
  _context5.prev = 7;
326
326
  _context5.t0 = _context5["catch"](1);
327
327
  if (!(_context5.t0 instanceof _blockService.BlockError)) {
328
- _context5.next = 11;
328
+ _context5.next = 13;
329
+ break;
330
+ }
331
+ if (!(_context5.t0.status === 404)) {
332
+ _context5.next = 12;
329
333
  break;
330
334
  }
335
+ return _context5.abrupt("return", {
336
+ resourceId: resourceId,
337
+ success: true
338
+ });
339
+ case 12:
331
340
  return _context5.abrupt("return", {
332
341
  resourceId: resourceId,
333
342
  success: false,
334
343
  error: mapBlockError(_context5.t0)
335
344
  });
336
- case 11:
345
+ case 13:
337
346
  return _context5.abrupt("return", {
338
347
  resourceId: resourceId,
339
348
  success: false,
340
349
  error: (0, _errorHandling.stringifyError)(_context5.t0)
341
350
  });
342
- case 12:
351
+ case 14:
343
352
  case "end":
344
353
  return _context5.stop();
345
354
  }
@@ -360,6 +369,56 @@ var BlockServiceADFWriteProvider = /*#__PURE__*/function () {
360
369
  value: function generateResourceId() {
361
370
  return crypto.randomUUID();
362
371
  }
372
+ }, {
373
+ key: "updateReferenceData",
374
+ value: function () {
375
+ var _updateReferenceData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(blocks, noContent) {
376
+ var _this = this;
377
+ return _regenerator.default.wrap(function _callee6$(_context6) {
378
+ while (1) switch (_context6.prev = _context6.next) {
379
+ case 0:
380
+ _context6.prev = 0;
381
+ _context6.next = 3;
382
+ return (0, _blockService.updateReferenceSyncedBlockOnDocument)({
383
+ documentAri: this.sourceAri,
384
+ blocks: blocks.map(function (block) {
385
+ return {
386
+ blockAri: (0, _ari.generateBlockAriFromReference)(_this.sourceAri, block.resourceId),
387
+ blockInstanceId: block.localId
388
+ };
389
+ }, noContent)
390
+ });
391
+ case 3:
392
+ return _context6.abrupt("return", {
393
+ success: true
394
+ });
395
+ case 6:
396
+ _context6.prev = 6;
397
+ _context6.t0 = _context6["catch"](0);
398
+ if (!(_context6.t0 instanceof _blockService.BlockError)) {
399
+ _context6.next = 10;
400
+ break;
401
+ }
402
+ return _context6.abrupt("return", {
403
+ success: false,
404
+ error: mapBlockError(_context6.t0)
405
+ });
406
+ case 10:
407
+ return _context6.abrupt("return", {
408
+ success: false,
409
+ error: (0, _errorHandling.stringifyError)(_context6.t0)
410
+ });
411
+ case 11:
412
+ case "end":
413
+ return _context6.stop();
414
+ }
415
+ }, _callee6, this, [[0, 6]]);
416
+ }));
417
+ function updateReferenceData(_x6, _x7) {
418
+ return _updateReferenceData.apply(this, arguments);
419
+ }
420
+ return updateReferenceData;
421
+ }()
363
422
  }]);
364
423
  }();
365
424
  /**
@@ -409,6 +409,13 @@ var ConfluenceADFWriteProvider = /*#__PURE__*/function () {
409
409
  value: function generateResourceIdForReference(sourceId) {
410
410
  return sourceId;
411
411
  }
412
+ }, {
413
+ key: "updateReferenceData",
414
+ value: function updateReferenceData(_blocks, _noContent) {
415
+ return Promise.resolve({
416
+ success: true
417
+ });
418
+ }
412
419
  }]);
413
420
  }();
414
421
  /**
@@ -296,6 +296,11 @@ var SyncBlockProvider = exports.SyncBlockProvider = /*#__PURE__*/function (_Sync
296
296
  throw new Error("".concat(sourceProduct, " source product not supported"));
297
297
  }
298
298
  }
299
+ }, {
300
+ key: "updateReferenceData",
301
+ value: function updateReferenceData(blocks, noContent) {
302
+ return this.writeProvider.updateReferenceData(blocks, noContent);
303
+ }
299
304
  }]);
300
305
  }(_types2.SyncBlockDataProvider);
301
306
  var useMemoizedSyncedBlockProvider = exports.useMemoizedSyncedBlockProvider = function useMemoizedSyncedBlockProvider(fetchProvider, writeProvider, sourceId, providerOptions, getSSRData) {
@@ -30,6 +30,10 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
30
30
  var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
31
31
  function ReferenceSyncBlockStoreManager(dataProvider, fireAnalyticsEvent) {
32
32
  (0, _classCallCheck2.default)(this, ReferenceSyncBlockStoreManager);
33
+ // Keeps track of addition and deletion of reference synced blocks on the document
34
+ // This starts as true to always flush the cache when document is saved for the first time
35
+ // to cater the case when a editor seesion is closed without document being updated right after reference block is deleted
36
+ (0, _defineProperty2.default)(this, "isCacheDirty", true);
33
37
  (0, _defineProperty2.default)(this, "isRefreshingSubscriptions", false);
34
38
  this.syncBlockCache = new Map();
35
39
  this.subscriptions = new Map();
@@ -327,6 +331,9 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
327
331
  // add to subscriptions map
328
332
  var resourceSubscriptions = this.subscriptions.get(resourceId) || {};
329
333
  this.subscriptions.set(resourceId, _objectSpread(_objectSpread({}, resourceSubscriptions), {}, (0, _defineProperty2.default)({}, localId, callback)));
334
+
335
+ // New subscription means new reference synced block is added to the document
336
+ this.isCacheDirty = true;
330
337
  var syncBlockNode = (0, _utils.createSyncBlockNode)(localId, resourceId);
331
338
 
332
339
  // call the callback immediately if we have cached data
@@ -346,6 +353,8 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
346
353
  return function () {
347
354
  var resourceSubscriptions = _this3.subscriptions.get(resourceId);
348
355
  if (resourceSubscriptions) {
356
+ // Unsubscription means a reference synced block is removed from the document
357
+ _this3.isCacheDirty = true;
349
358
  delete resourceSubscriptions[localId];
350
359
  if (Object.keys(resourceSubscriptions).length === 0) {
351
360
  _this3.subscriptions.delete(resourceId);
@@ -515,6 +524,89 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
515
524
  }
516
525
  }
517
526
  }
527
+
528
+ /**
529
+ * Update reference synced blocks on the document with the BE
530
+ *
531
+ * @returns true if the reference synced blocks are updated successfully, false otherwise
532
+ */
533
+ }, {
534
+ key: "flush",
535
+ value: (function () {
536
+ var _flush = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
537
+ var success, blocks, updateResult, _this$fireAnalyticsEv9, _this$fireAnalyticsEv0;
538
+ return _regenerator.default.wrap(function _callee3$(_context4) {
539
+ while (1) switch (_context4.prev = _context4.next) {
540
+ case 0:
541
+ if (this.isCacheDirty) {
542
+ _context4.next = 2;
543
+ break;
544
+ }
545
+ return _context4.abrupt("return", true);
546
+ case 2:
547
+ success = true;
548
+ _context4.prev = 3;
549
+ if (this.dataProvider) {
550
+ _context4.next = 6;
551
+ break;
552
+ }
553
+ throw new Error('Data provider not set');
554
+ case 6:
555
+ blocks = []; // Collect all reference synced blocks on the current document
556
+ Array.from(this.subscriptions.entries()).forEach(function (_ref2) {
557
+ var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
558
+ resourceId = _ref3[0],
559
+ callbacks = _ref3[1];
560
+ Object.keys(callbacks).forEach(function (localId) {
561
+ blocks.push({
562
+ resourceId: resourceId,
563
+ localId: localId
564
+ });
565
+ });
566
+ });
567
+ if (!(blocks.length === 0)) {
568
+ _context4.next = 10;
569
+ break;
570
+ }
571
+ return _context4.abrupt("return", true);
572
+ case 10:
573
+ _context4.next = 12;
574
+ return this.dataProvider.updateReferenceData(blocks);
575
+ case 12:
576
+ updateResult = _context4.sent;
577
+ if (!updateResult.success) {
578
+ success = false;
579
+ (_this$fireAnalyticsEv9 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv9 === void 0 || _this$fireAnalyticsEv9.call(this, (0, _errorHandling.updateReferenceErrorPayload)(updateResult.error || 'Failed to update reference synced blocks on the document'));
580
+ }
581
+ _context4.next = 21;
582
+ break;
583
+ case 16:
584
+ _context4.prev = 16;
585
+ _context4.t0 = _context4["catch"](3);
586
+ success = false;
587
+ (0, _monitoring.logException)(_context4.t0, {
588
+ location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
589
+ });
590
+ (_this$fireAnalyticsEv0 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv0 === void 0 || _this$fireAnalyticsEv0.call(this, (0, _errorHandling.updateReferenceErrorPayload)(_context4.t0.message));
591
+ case 21:
592
+ _context4.prev = 21;
593
+ if (success) {
594
+ this.isCacheDirty = false;
595
+ }
596
+ return _context4.finish(21);
597
+ case 24:
598
+ return _context4.abrupt("return", success);
599
+ case 25:
600
+ case "end":
601
+ return _context4.stop();
602
+ }
603
+ }, _callee3, this, [[3, 16, 21, 24]]);
604
+ }));
605
+ function flush() {
606
+ return _flush.apply(this, arguments);
607
+ }
608
+ return flush;
609
+ }())
518
610
  }, {
519
611
  key: "destroy",
520
612
  value: function destroy() {
@@ -12,9 +12,11 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
12
12
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
13
  var _uuid = _interopRequireDefault(require("uuid"));
14
14
  var _monitoring = require("@atlaskit/editor-common/monitoring");
15
+ var _types = require("../common/types");
15
16
  var _errorHandling = require("../utils/errorHandling");
16
17
  var _utils = require("../utils/utils");
17
- // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
18
+ 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; }
19
+ 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; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
18
20
  // A store manager responsible for the lifecycle and state management of source sync blocks in an editor instance.
19
21
  // Designed to manage local in-memory state and synchronize with an external data provider.
20
22
  // Supports create, flush, and delete operations for source sync blocks.
@@ -58,7 +60,9 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
58
60
  throw new Error('Local ID or resource ID is not set');
59
61
  }
60
62
  var syncBlockData = (0, _utils.convertSyncBlockPMNodeToSyncBlockData)(syncBlockNode);
61
- this.syncBlockCache.set(resourceId, syncBlockData);
63
+ this.syncBlockCache.set(resourceId, _objectSpread(_objectSpread({}, syncBlockData), {}, {
64
+ isDirty: true
65
+ }));
62
66
  return true;
63
67
  } catch (error) {
64
68
  var _this$fireAnalyticsEv;
@@ -95,7 +99,8 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
95
99
  bodiedSyncBlockData = [];
96
100
  Array.from(this.syncBlockCache.values()).forEach(function (syncBlockData) {
97
101
  // Don't flush nodes that are waiting to be deleted to avoid nodes being re-created
98
- if (!syncBlockData.pendingDeletion) {
102
+ // Don't flush nodes that haven't been updated since we last flushed
103
+ if (!syncBlockData.pendingDeletion && syncBlockData.isDirty) {
99
104
  bodiedSyncBlockNodes.push({
100
105
  type: 'bodiedSyncBlock',
101
106
  attrs: {
@@ -116,37 +121,46 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
116
121
  return this.dataProvider.writeNodesData(bodiedSyncBlockNodes, bodiedSyncBlockData);
117
122
  case 10:
118
123
  writeResults = _context.sent;
124
+ writeResults.forEach(function (result) {
125
+ // set isDirty to false on write success and unrecoverable errors like not found
126
+ if (result.resourceId && (result.error === _types.SyncBlockError.NotFound || !result.error)) {
127
+ var cachedData = _this2.syncBlockCache.get(result.resourceId);
128
+ if (cachedData) {
129
+ cachedData.isDirty = false;
130
+ }
131
+ }
132
+ });
119
133
  if (!writeResults.every(function (result) {
120
- return result.resourceId !== undefined;
134
+ return result.resourceId && !result.error;
121
135
  })) {
122
- _context.next = 15;
136
+ _context.next = 16;
123
137
  break;
124
138
  }
125
139
  return _context.abrupt("return", true);
126
- case 15:
140
+ case 16:
127
141
  writeResults.filter(function (result) {
128
- return result.resourceId === undefined;
142
+ return !result.resourceId || result.error;
129
143
  }).forEach(function (result) {
130
144
  var _this2$fireAnalyticsE;
131
145
  (_this2$fireAnalyticsE = _this2.fireAnalyticsEvent) === null || _this2$fireAnalyticsE === void 0 || _this2$fireAnalyticsE.call(_this2, (0, _errorHandling.updateErrorPayload)(result.error || 'Failed to write data'));
132
146
  });
133
147
  return _context.abrupt("return", false);
134
- case 17:
135
- _context.next = 24;
148
+ case 18:
149
+ _context.next = 25;
136
150
  break;
137
- case 19:
138
- _context.prev = 19;
151
+ case 20:
152
+ _context.prev = 20;
139
153
  _context.t0 = _context["catch"](0);
140
154
  (0, _monitoring.logException)(_context.t0, {
141
155
  location: 'editor-synced-block-provider/sourceSyncBlockStoreManager'
142
156
  });
143
157
  (_this$fireAnalyticsEv2 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv2 === void 0 || _this$fireAnalyticsEv2.call(this, (0, _errorHandling.updateErrorPayload)(_context.t0.message));
144
158
  return _context.abrupt("return", false);
145
- case 24:
159
+ case 25:
146
160
  case "end":
147
161
  return _context.stop();
148
162
  }
149
- }, _callee, this, [[0, 19]]);
163
+ }, _callee, this, [[0, 20]]);
150
164
  }));
151
165
  function flush() {
152
166
  return _flush.apply(this, arguments);
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.getSourceInfoErrorPayload = exports.getErrorPayload = exports.fetchErrorPayload = exports.deleteErrorPayload = exports.createErrorPayload = void 0;
6
+ exports.updateReferenceErrorPayload = exports.updateErrorPayload = exports.updateCacheErrorPayload = exports.stringifyError = exports.getSourceInfoErrorPayload = exports.getErrorPayload = exports.fetchErrorPayload = exports.deleteErrorPayload = exports.createErrorPayload = void 0;
7
7
  var _analytics = require("@atlaskit/editor-common/analytics");
8
8
  var stringifyError = exports.stringifyError = function stringifyError(error) {
9
9
  try {
@@ -32,6 +32,9 @@ var getSourceInfoErrorPayload = exports.getSourceInfoErrorPayload = function get
32
32
  var updateErrorPayload = exports.updateErrorPayload = function updateErrorPayload(error) {
33
33
  return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error);
34
34
  };
35
+ var updateReferenceErrorPayload = exports.updateReferenceErrorPayload = function updateReferenceErrorPayload(error) {
36
+ return getErrorPayload(_analytics.ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error);
37
+ };
35
38
  var createErrorPayload = exports.createErrorPayload = function createErrorPayload(error) {
36
39
  return getErrorPayload(_analytics.ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error);
37
40
  };
@@ -127,4 +127,23 @@ export const createSyncedBlock = async ({
127
127
  throw new BlockError(response.status);
128
128
  }
129
129
  return await response.json();
130
+ };
131
+ export const updateReferenceSyncedBlockOnDocument = async ({
132
+ documentAri,
133
+ blocks,
134
+ noContent = true
135
+ }) => {
136
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/document/${encodeURIComponent(documentAri)}/references?noContent=${noContent}`, {
137
+ method: 'PUT',
138
+ headers: COMMON_HEADERS,
139
+ body: JSON.stringify({
140
+ blocks
141
+ })
142
+ });
143
+ if (!response.ok) {
144
+ throw new BlockError(response.status);
145
+ }
146
+ if (!noContent) {
147
+ return await response.json();
148
+ }
130
149
  };
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable require-unicode-regexp */
2
2
  import { useMemo } from 'react';
3
3
  import { generateBlockAri, generateBlockAriFromReference } from '../../clients/block-service/ari';
4
- import { BlockError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getSyncedBlockContent, updateSyncedBlock } from '../../clients/block-service/blockService';
4
+ import { BlockError, createSyncedBlock, deleteSyncedBlock, getReferenceSyncedBlocks, getSyncedBlockContent, updateReferenceSyncedBlockOnDocument, updateSyncedBlock } from '../../clients/block-service/blockService';
5
5
  import { SyncBlockError } from '../../common/types';
6
6
  import { stringifyError } from '../../utils/errorHandling';
7
7
  const mapBlockError = error => {
@@ -212,6 +212,14 @@ class BlockServiceADFWriteProvider {
212
212
  };
213
213
  } catch (error) {
214
214
  if (error instanceof BlockError) {
215
+ if (error.status === 404) {
216
+ // User should not be blocked by not_found error when deleting,
217
+ // hence returns successful result for 404 error
218
+ return {
219
+ resourceId,
220
+ success: true
221
+ };
222
+ }
215
223
  return {
216
224
  resourceId,
217
225
  success: false,
@@ -233,6 +241,31 @@ class BlockServiceADFWriteProvider {
233
241
  generateResourceId() {
234
242
  return crypto.randomUUID();
235
243
  }
244
+ async updateReferenceData(blocks, noContent) {
245
+ try {
246
+ await updateReferenceSyncedBlockOnDocument({
247
+ documentAri: this.sourceAri,
248
+ blocks: blocks.map(block => ({
249
+ blockAri: generateBlockAriFromReference(this.sourceAri, block.resourceId),
250
+ blockInstanceId: block.localId
251
+ }), noContent)
252
+ });
253
+ return {
254
+ success: true
255
+ };
256
+ } catch (error) {
257
+ if (error instanceof BlockError) {
258
+ return {
259
+ success: false,
260
+ error: mapBlockError(error)
261
+ };
262
+ }
263
+ return {
264
+ success: false,
265
+ error: stringifyError(error)
266
+ };
267
+ }
268
+ }
236
269
  }
237
270
 
238
271
  /**
@@ -275,6 +275,11 @@ class ConfluenceADFWriteProvider {
275
275
  generateResourceIdForReference(sourceId) {
276
276
  return sourceId;
277
277
  }
278
+ updateReferenceData(_blocks, _noContent) {
279
+ return Promise.resolve({
280
+ success: true
281
+ });
282
+ }
278
283
  }
279
284
 
280
285
  /**
@@ -200,6 +200,9 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
200
200
  throw new Error(`${sourceProduct} source product not supported`);
201
201
  }
202
202
  }
203
+ updateReferenceData(blocks, noContent) {
204
+ return this.writeProvider.updateReferenceData(blocks, noContent);
205
+ }
203
206
  }
204
207
  export const useMemoizedSyncedBlockProvider = (fetchProvider, writeProvider, sourceId, providerOptions, getSSRData) => {
205
208
  return useMemo(() => {
@@ -2,7 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { logException } from '@atlaskit/editor-common/monitoring';
3
3
  import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
4
4
  import { SyncBlockError } from '../common/types';
5
- import { fetchErrorPayload, getSourceInfoErrorPayload } from '../utils/errorHandling';
5
+ import { fetchErrorPayload, getSourceInfoErrorPayload, updateReferenceErrorPayload } from '../utils/errorHandling';
6
6
  import { resolveSyncBlockInstance } from '../utils/resolveSyncBlockInstance';
7
7
  import { createSyncBlockNode } from '../utils/utils';
8
8
 
@@ -13,6 +13,10 @@ import { createSyncBlockNode } from '../utils/utils';
13
13
  // Can be used in both editor and renderer contexts.
14
14
  export class ReferenceSyncBlockStoreManager {
15
15
  constructor(dataProvider, fireAnalyticsEvent) {
16
+ // Keeps track of addition and deletion of reference synced blocks on the document
17
+ // This starts as true to always flush the cache when document is saved for the first time
18
+ // to cater the case when a editor seesion is closed without document being updated right after reference block is deleted
19
+ _defineProperty(this, "isCacheDirty", true);
16
20
  _defineProperty(this, "isRefreshingSubscriptions", false);
17
21
  this.syncBlockCache = new Map();
18
22
  this.subscriptions = new Map();
@@ -212,6 +216,9 @@ export class ReferenceSyncBlockStoreManager {
212
216
  ...resourceSubscriptions,
213
217
  [localId]: callback
214
218
  });
219
+
220
+ // New subscription means new reference synced block is added to the document
221
+ this.isCacheDirty = true;
215
222
  const syncBlockNode = createSyncBlockNode(localId, resourceId);
216
223
 
217
224
  // call the callback immediately if we have cached data
@@ -231,6 +238,8 @@ export class ReferenceSyncBlockStoreManager {
231
238
  return () => {
232
239
  const resourceSubscriptions = this.subscriptions.get(resourceId);
233
240
  if (resourceSubscriptions) {
241
+ // Unsubscription means a reference synced block is removed from the document
242
+ this.isCacheDirty = true;
234
243
  delete resourceSubscriptions[localId];
235
244
  if (Object.keys(resourceSubscriptions).length === 0) {
236
245
  this.subscriptions.delete(resourceId);
@@ -397,6 +406,55 @@ export class ReferenceSyncBlockStoreManager {
397
406
  }
398
407
  }
399
408
  }
409
+
410
+ /**
411
+ * Update reference synced blocks on the document with the BE
412
+ *
413
+ * @returns true if the reference synced blocks are updated successfully, false otherwise
414
+ */
415
+ async flush() {
416
+ if (!this.isCacheDirty) {
417
+ return true;
418
+ }
419
+ let success = true;
420
+ try {
421
+ if (!this.dataProvider) {
422
+ throw new Error('Data provider not set');
423
+ }
424
+ const blocks = [];
425
+
426
+ // Collect all reference synced blocks on the current document
427
+ Array.from(this.subscriptions.entries()).forEach(([resourceId, callbacks]) => {
428
+ Object.keys(callbacks).forEach(localId => {
429
+ blocks.push({
430
+ resourceId,
431
+ localId
432
+ });
433
+ });
434
+ });
435
+ if (blocks.length === 0) {
436
+ return true;
437
+ }
438
+ const updateResult = await this.dataProvider.updateReferenceData(blocks);
439
+ if (!updateResult.success) {
440
+ var _this$fireAnalyticsEv10;
441
+ success = false;
442
+ (_this$fireAnalyticsEv10 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv10 === void 0 ? void 0 : _this$fireAnalyticsEv10.call(this, updateReferenceErrorPayload(updateResult.error || 'Failed to update reference synced blocks on the document'));
443
+ }
444
+ } catch (error) {
445
+ var _this$fireAnalyticsEv11;
446
+ success = false;
447
+ logException(error, {
448
+ location: 'editor-synced-block-provider/referenceSyncBlockStoreManager'
449
+ });
450
+ (_this$fireAnalyticsEv11 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv11 === void 0 ? void 0 : _this$fireAnalyticsEv11.call(this, updateReferenceErrorPayload(error.message));
451
+ } finally {
452
+ if (success) {
453
+ this.isCacheDirty = false;
454
+ }
455
+ }
456
+ return success;
457
+ }
400
458
  destroy() {
401
459
  this.dataProvider = undefined;
402
460
  this.syncBlockCache.clear();