@atlaskit/editor-synced-block-provider 2.15.2 → 2.15.4

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 (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/clients/block-service/blockService.js +42 -1
  3. package/dist/cjs/index.js +14 -1
  4. package/dist/cjs/providers/block-service/blockServiceAPI.js +62 -3
  5. package/dist/cjs/providers/confluence/confluenceContentAPI.js +7 -0
  6. package/dist/cjs/providers/syncBlockProvider.js +5 -0
  7. package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +92 -0
  8. package/dist/cjs/utils/errorHandling.js +4 -1
  9. package/dist/cjs/utils/utils.js +19 -1
  10. package/dist/es2019/clients/block-service/blockService.js +19 -0
  11. package/dist/es2019/index.js +2 -1
  12. package/dist/es2019/providers/block-service/blockServiceAPI.js +34 -1
  13. package/dist/es2019/providers/confluence/confluenceContentAPI.js +5 -0
  14. package/dist/es2019/providers/syncBlockProvider.js +3 -0
  15. package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +59 -1
  16. package/dist/es2019/utils/errorHandling.js +1 -0
  17. package/dist/es2019/utils/utils.js +18 -0
  18. package/dist/esm/clients/block-service/blockService.js +41 -0
  19. package/dist/esm/index.js +2 -1
  20. package/dist/esm/providers/block-service/blockServiceAPI.js +63 -4
  21. package/dist/esm/providers/confluence/confluenceContentAPI.js +7 -0
  22. package/dist/esm/providers/syncBlockProvider.js +5 -0
  23. package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +93 -1
  24. package/dist/esm/utils/errorHandling.js +3 -0
  25. package/dist/esm/utils/utils.js +18 -0
  26. package/dist/types/clients/block-service/blockService.d.ts +16 -4
  27. package/dist/types/index.d.ts +2 -1
  28. package/dist/types/providers/block-service/blockServiceAPI.d.ts +3 -2
  29. package/dist/types/providers/confluence/confluenceContentAPI.d.ts +3 -2
  30. package/dist/types/providers/syncBlockProvider.d.ts +3 -2
  31. package/dist/types/providers/types.d.ts +13 -1
  32. package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +7 -0
  33. package/dist/types/utils/errorHandling.d.ts +1 -0
  34. package/dist/types/utils/utils.d.ts +5 -1
  35. package/dist/types-ts4.5/clients/block-service/blockService.d.ts +16 -4
  36. package/dist/types-ts4.5/index.d.ts +2 -1
  37. package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +3 -2
  38. package/dist/types-ts4.5/providers/confluence/confluenceContentAPI.d.ts +3 -2
  39. package/dist/types-ts4.5/providers/syncBlockProvider.d.ts +3 -2
  40. package/dist/types-ts4.5/providers/types.d.ts +13 -1
  41. package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +7 -0
  42. package/dist/types-ts4.5/utils/errorHandling.d.ts +1 -0
  43. package/dist/types-ts4.5/utils/utils.d.ts +5 -1
  44. package/package.json +3 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 2.15.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`a41bf96788d92`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a41bf96788d92) -
8
+ [ux] Fix sync block permissions denied error to show the correct state
9
+
10
+ ## 2.15.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [`f14e76661c943`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f14e76661c943) -
15
+ [EDITOR-2844] Save reference synced block on document to BE when a page is saved
16
+ - Updated dependencies
17
+
3
18
  ## 2.15.2
4
19
 
5
20
  ### 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
  }();
package/dist/cjs/index.js CHANGED
@@ -63,6 +63,12 @@ Object.defineProperty(exports, "fetchConfluencePageInfo", {
63
63
  return _sourceInfo.fetchConfluencePageInfo;
64
64
  }
65
65
  });
66
+ Object.defineProperty(exports, "fetchErrorPayload", {
67
+ enumerable: true,
68
+ get: function get() {
69
+ return _errorHandling.fetchErrorPayload;
70
+ }
71
+ });
66
72
  Object.defineProperty(exports, "fetchReferences", {
67
73
  enumerable: true,
68
74
  get: function get() {
@@ -87,6 +93,12 @@ Object.defineProperty(exports, "getConfluencePageAri", {
87
93
  return _ari2.getConfluencePageAri;
88
94
  }
89
95
  });
96
+ Object.defineProperty(exports, "getContentIdAndProductFromResourceId", {
97
+ enumerable: true,
98
+ get: function get() {
99
+ return _utils.getContentIdAndProductFromResourceId;
100
+ }
101
+ });
90
102
  Object.defineProperty(exports, "getLocalIdFromBlockResourceId", {
91
103
  enumerable: true,
92
104
  get: function get() {
@@ -179,4 +191,5 @@ var _syncBlockProvider = require("./providers/syncBlockProvider");
179
191
  var _referenceSyncBlockStoreManager = require("./store-manager/referenceSyncBlockStoreManager");
180
192
  var _syncBlockStoreManager = require("./store-manager/syncBlockStoreManager");
181
193
  var _resolveSyncBlockInstance = require("./utils/resolveSyncBlockInstance");
182
- var _utils = require("./utils/utils");
194
+ var _utils = require("./utils/utils");
195
+ var _errorHandling = require("./utils/errorHandling");
@@ -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() {
@@ -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
  };
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.createSyncBlockNode = exports.convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockJSONNodeToSyncBlockNode = exports.convertPMNodesToSyncBlockNodes = exports.convertPMNodeToSyncBlockNode = void 0;
6
+ exports.getContentIdAndProductFromResourceId = exports.createSyncBlockNode = exports.convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockJSONNodeToSyncBlockNode = exports.convertPMNodesToSyncBlockNodes = exports.convertPMNodeToSyncBlockNode = void 0;
7
+ /* eslint-disable require-unicode-regexp */
8
+
7
9
  var convertSyncBlockPMNodeToSyncBlockData = exports.convertSyncBlockPMNodeToSyncBlockData = function convertSyncBlockPMNodeToSyncBlockData(node) {
8
10
  return {
9
11
  blockInstanceId: node.attrs.localId,
@@ -39,4 +41,20 @@ var convertPMNodesToSyncBlockNodes = exports.convertPMNodesToSyncBlockNodes = fu
39
41
  }).filter(function (node) {
40
42
  return node !== undefined;
41
43
  }) || [];
44
+ };
45
+
46
+ /*
47
+ * From a reference block resource id (the resourceId stored in the node attributes)
48
+ * e.g. confluence-page/5769323474/cdf6a1bc-b241-487a-93e9-e30bde363cbc
49
+ * Extracts the source page content id and source product
50
+ */
51
+ var getContentIdAndProductFromResourceId = exports.getContentIdAndProductFromResourceId = function getContentIdAndProductFromResourceId(resourceId) {
52
+ var match = resourceId.match(/^(confluence-page|jira-work-item)\/([^/]+)/);
53
+ if (match !== null && match !== void 0 && match[2]) {
54
+ return {
55
+ sourceProduct: match[1],
56
+ sourceContentId: match[2]
57
+ };
58
+ }
59
+ throw new Error("Invalid resourceId: ".concat(resourceId));
42
60
  };
@@ -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
  };
@@ -23,5 +23,6 @@ export { SyncBlockStoreManager } from './store-manager/syncBlockStoreManager';
23
23
 
24
24
  // utils
25
25
  export { resolveSyncBlockInstance } from './utils/resolveSyncBlockInstance';
26
- export { createSyncBlockNode, convertSyncBlockPMNodeToSyncBlockData, convertSyncBlockJSONNodeToSyncBlockNode, convertPMNodesToSyncBlockNodes } from './utils/utils';
26
+ export { createSyncBlockNode, convertSyncBlockPMNodeToSyncBlockData, convertSyncBlockJSONNodeToSyncBlockNode, convertPMNodesToSyncBlockNodes, getContentIdAndProductFromResourceId } from './utils/utils';
27
+ export { fetchErrorPayload } from './utils/errorHandling';
27
28
  export { fetchReferences } from './providers/block-service/blockServiceAPI';
@@ -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();
@@ -18,6 +18,7 @@ export const getErrorPayload = (actionSubjectId, error) => ({
18
18
  export const fetchErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_FETCH, error);
19
19
  export const getSourceInfoErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_GET_SOURCE_INFO, error);
20
20
  export const updateErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE, error);
21
+ export const updateReferenceErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.REFERENCE_SYNCED_BLOCK_UPDATE, error);
21
22
  export const createErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_CREATE, error);
22
23
  export const deleteErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_DELETE, error);
23
24
  export const updateCacheErrorPayload = error => getErrorPayload(ACTION_SUBJECT_ID.SYNCED_BLOCK_UPDATE_CACHE, error);
@@ -1,3 +1,5 @@
1
+ /* eslint-disable require-unicode-regexp */
2
+
1
3
  export const convertSyncBlockPMNodeToSyncBlockData = node => {
2
4
  return {
3
5
  blockInstanceId: node.attrs.localId,
@@ -29,4 +31,20 @@ export const convertPMNodeToSyncBlockNode = node => {
29
31
  };
30
32
  export const convertPMNodesToSyncBlockNodes = nodes => {
31
33
  return nodes.map(node => convertPMNodeToSyncBlockNode(node)).filter(node => node !== undefined) || [];
34
+ };
35
+
36
+ /*
37
+ * From a reference block resource id (the resourceId stored in the node attributes)
38
+ * e.g. confluence-page/5769323474/cdf6a1bc-b241-487a-93e9-e30bde363cbc
39
+ * Extracts the source page content id and source product
40
+ */
41
+ export const getContentIdAndProductFromResourceId = resourceId => {
42
+ const match = resourceId.match(/^(confluence-page|jira-work-item)\/([^/]+)/);
43
+ if (match !== null && match !== void 0 && match[2]) {
44
+ return {
45
+ sourceProduct: match[1],
46
+ sourceContentId: match[2]
47
+ };
48
+ }
49
+ throw new Error(`Invalid resourceId: ${resourceId}`);
32
50
  };