@atlaskit/collab-provider 9.17.2 → 9.17.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atlaskit/collab-provider
2
2
 
3
+ ## 9.17.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#57317](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/57317) [`d989b4568594`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/d989b4568594) - ESS-4149: Add send steps queue under FF
8
+ - Updated dependencies
9
+
10
+ ## 9.17.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [#56940](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/56940) [`e862ee8c3290`](https://bitbucket.org/atlassian/atlassian-frontend/commits/e862ee8c3290) - Add success analytics for updating document
15
+ - [#57024](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/57024) [`4807b15145e2`](https://bitbucket.org/atlassian/atlassian-frontend/commits/4807b15145e2) - Update commented link for update document action
16
+
3
17
  ## 9.17.2
4
18
 
5
19
  ### Patch Changes
@@ -20,6 +20,7 @@ var _editorJsonTransformer = require("@atlaskit/editor-json-transformer");
20
20
  var _provider = require("../provider");
21
21
  var _catchup = require("./catchup");
22
22
  var _stepQueueState = require("./step-queue-state");
23
+ var _featureFlags = require("../feature-flags");
23
24
  var _errorTypes = require("../errors/error-types");
24
25
  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; }
25
26
  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; }
@@ -43,11 +44,12 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
43
44
  * @param onErrorHandled - Callback to handle
44
45
  * @param metadataService
45
46
  * @param enableErrorOnFailedDocumentApply - Enable failed document update exceptions.
47
+ * @param enableSendStepsQueue - Enable send steps queue.
46
48
  */
47
49
  function DocumentService(participantsService, analyticsHelper, fetchCatchup, fetchReconcile, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService) {
48
50
  var _this = this;
49
51
  var enableErrorOnFailedDocumentApply = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : false;
50
- var enableSendStepsQueue = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : false;
52
+ var enableSendStepsQueue = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : (0, _featureFlags.getCollabProviderFeatureFlag)('sendStepsQueueFF');
51
53
  (0, _classCallCheck2.default)(this, DocumentService);
52
54
  // Fires analytics to editor when collab editor cannot sync up
53
55
  (0, _defineProperty2.default)(this, "stepRejectCounter", 0);
@@ -112,7 +114,7 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
112
114
  _context.prev = 16;
113
115
  _this.stepQueue.resumeQueue();
114
116
  _this.processQueue();
115
- _this.sendStepsFromCurrentState(); // this will eventually retry catchup as it calls commitStep which will either catchup on onStepsAdded or onErrorHandled
117
+ _this.sendStepsFromCurrentState(); // this will eventually retry catchup as it calls throttledCommitStep which will either catchup on onStepsAdded or onErrorHandled
116
118
  _this.stepRejectCounter = 0;
117
119
  return _context.finish(16);
118
120
  case 22:
@@ -353,10 +355,13 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
353
355
  }, reserveCursor ? {
354
356
  reserveCursor: reserveCursor
355
357
  } : {}));
358
+ _this.updateDocumentAnalytics(doc, version);
359
+ });
360
+ (0, _defineProperty2.default)(this, "updateDocumentAnalytics", function (doc, version) {
356
361
  var updatedVersion = _this.getCurrentPmVersion();
362
+ var isDocContentValid = _this.validatePMJSONDocument(doc);
357
363
  if (_this.getCurrentPmVersion() !== version) {
358
364
  var _doc$content, _this$analyticsHelper17;
359
- var isDocContentValid = _this.validatePMJSONDocument(doc);
360
365
  var _error = new _errorTypes.UpdateDocumentError('Failed to update the document', {
361
366
  newVersion: version,
362
367
  editorVersion: updatedVersion,
@@ -380,14 +385,23 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
380
385
  throw _error;
381
386
  }
382
387
  // Otherwise just fail silently for now
388
+ } else {
389
+ var _this$analyticsHelper18, _doc$content2;
390
+ (_this$analyticsHelper18 = _this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 || _this$analyticsHelper18.sendActionEvent(_const.EVENT_ACTION.UPDATE_DOCUMENT, _const.EVENT_STATUS.SUCCESS, {
391
+ newVersion: version,
392
+ editorVersion: updatedVersion,
393
+ isDocTruthy: !!doc,
394
+ docHasContent: (doc === null || doc === void 0 || (_doc$content2 = doc.content) === null || _doc$content2 === void 0 ? void 0 : _doc$content2.length) >= 1,
395
+ isDocContentValid: isDocContentValid
396
+ });
383
397
  }
384
398
  });
385
399
  (0, _defineProperty2.default)(this, "validatePMJSONDocument", function (doc) {
386
400
  try {
387
401
  var _this$getState5;
388
402
  if (!((_this$getState5 = _this.getState) !== null && _this$getState5 !== void 0 && _this$getState5.call(_this))) {
389
- var _this$analyticsHelper18;
390
- (_this$analyticsHelper18 = _this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 || _this$analyticsHelper18.sendErrorEvent(new Error('Editor state is undefined'), 'validatePMJSONDocument called without state');
403
+ var _this$analyticsHelper19;
404
+ (_this$analyticsHelper19 = _this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 || _this$analyticsHelper19.sendErrorEvent(new Error('Editor state is undefined'), 'validatePMJSONDocument called without state');
391
405
  }
392
406
  var state = _this.getState();
393
407
  var content = (doc.content || []).map(function (child) {
@@ -410,7 +424,7 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
410
424
  * @throws {Error} Couldn't sync the steps after retrying 30 times
411
425
  */
412
426
  (0, _defineProperty2.default)(this, "commitUnconfirmedSteps", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
413
- var unconfirmedSteps, _this$getState6, _this$analyticsHelper20, count, unconfirmedTrs, lastTr, isLastTrConfirmed, _this$analyticsHelper19, nextUnconfirmedSteps, nextUnconfirmedTrs, _this$getUnconfirmedS, state, unconfirmedStepsInfoUGCRemoved, _error2, measure, _this$analyticsHelper21, _this$analyticsHelper22, _measure3;
427
+ var unconfirmedSteps, _this$getState6, _this$analyticsHelper21, count, unconfirmedTrs, lastTr, isLastTrConfirmed, _this$analyticsHelper20, nextUnconfirmedSteps, nextUnconfirmedTrs, _this$getUnconfirmedS, state, unconfirmedStepsInfoUGCRemoved, _error2, measure, _this$analyticsHelper22, _this$analyticsHelper23, _measure3;
414
428
  return _regenerator.default.wrap(function _callee4$(_context4) {
415
429
  while (1) switch (_context4.prev = _context4.next) {
416
430
  case 0:
@@ -429,7 +443,7 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
429
443
  lastTr = unconfirmedTrs === null || unconfirmedTrs === void 0 ? void 0 : unconfirmedTrs[unconfirmedTrs.length - 1];
430
444
  isLastTrConfirmed = false;
431
445
  if (!((_this$getState6 = _this.getState) !== null && _this$getState6 !== void 0 && _this$getState6.call(_this))) {
432
- (_this$analyticsHelper19 = _this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 || _this$analyticsHelper19.sendErrorEvent(new Error('Editor state is undefined'), 'commitUnconfirmedSteps called without state');
446
+ (_this$analyticsHelper20 = _this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 || _this$analyticsHelper20.sendErrorEvent(new Error('Editor state is undefined'), 'commitUnconfirmedSteps called without state');
433
447
  }
434
448
  case 9:
435
449
  if (isLastTrConfirmed) {
@@ -475,7 +489,7 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
475
489
  break;
476
490
  case 22:
477
491
  measure = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, _this.analyticsHelper);
478
- (_this$analyticsHelper20 = _this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 || _this$analyticsHelper20.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.SUCCESS, {
492
+ (_this$analyticsHelper21 = _this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 || _this$analyticsHelper21.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.SUCCESS, {
479
493
  latency: measure === null || measure === void 0 ? void 0 : measure.duration,
480
494
  // upon success, emit the total number of unconfirmed steps we synced
481
495
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
@@ -487,11 +501,11 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
487
501
  _context4.prev = 26;
488
502
  _context4.t0 = _context4["catch"](1);
489
503
  _measure3 = (0, _performance.stopMeasure)(_performance.MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, _this.analyticsHelper);
490
- (_this$analyticsHelper21 = _this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 || _this$analyticsHelper21.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.FAILURE, {
504
+ (_this$analyticsHelper22 = _this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 || _this$analyticsHelper22.sendActionEvent(_const.EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, _const.EVENT_STATUS.FAILURE, {
491
505
  latency: _measure3 === null || _measure3 === void 0 ? void 0 : _measure3.duration,
492
506
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
493
507
  });
494
- (_this$analyticsHelper22 = _this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 || _this$analyticsHelper22.sendErrorEvent(_context4.t0, 'Error while committing unconfirmed steps');
508
+ (_this$analyticsHelper23 = _this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 || _this$analyticsHelper23.sendErrorEvent(_context4.t0, 'Error while committing unconfirmed steps');
495
509
  throw _context4.t0;
496
510
  case 32:
497
511
  case "end":
@@ -500,17 +514,17 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
500
514
  }, _callee4, null, [[1, 26]]);
501
515
  })));
502
516
  (0, _defineProperty2.default)(this, "onStepRejectedError", function () {
503
- var _this$analyticsHelper23;
517
+ var _this$analyticsHelper24;
504
518
  _this.stepRejectCounter++;
505
519
  logger("Steps rejected (tries=".concat(_this.stepRejectCounter, ")"));
506
- (_this$analyticsHelper23 = _this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 || _this$analyticsHelper23.sendActionEvent(_const.EVENT_ACTION.SEND_STEPS_RETRY, _const.EVENT_STATUS.INFO, {
520
+ (_this$analyticsHelper24 = _this.analyticsHelper) === null || _this$analyticsHelper24 === void 0 || _this$analyticsHelper24.sendActionEvent(_const.EVENT_ACTION.SEND_STEPS_RETRY, _const.EVENT_STATUS.INFO, {
507
521
  count: _this.stepRejectCounter
508
522
  });
509
523
  var maxRetries = _this.aggressiveCatchup ? _provider.MAX_STEP_REJECTED_ERROR_AGGRESSIVE : _provider.MAX_STEP_REJECTED_ERROR;
510
524
  if (_this.stepRejectCounter >= maxRetries) {
511
- var _this$analyticsHelper24;
525
+ var _this$analyticsHelper25;
512
526
  logger("The steps were rejected too many times (tries=".concat(_this.stepRejectCounter, ", limit=").concat(_provider.MAX_STEP_REJECTED_ERROR, "). Trying to catch-up."));
513
- (_this$analyticsHelper24 = _this.analyticsHelper) === null || _this$analyticsHelper24 === void 0 || _this$analyticsHelper24.sendActionEvent(_const.EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, _const.EVENT_STATUS.INFO);
527
+ (_this$analyticsHelper25 = _this.analyticsHelper) === null || _this$analyticsHelper25 === void 0 || _this$analyticsHelper25.sendActionEvent(_const.EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, _const.EVENT_STATUS.INFO);
514
528
  _this.throttledCatchup();
515
529
  } else {
516
530
  // If committing steps failed try again automatically in 1s
@@ -584,9 +598,9 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
584
598
  }, 100);
585
599
  }
586
600
  } catch (error) {
587
- var _this$analyticsHelper25;
601
+ var _this$analyticsHelper26;
588
602
  logger("Processing steps failed with error: ".concat(error, ". Triggering catch up call."));
589
- (_this$analyticsHelper25 = this.analyticsHelper) === null || _this$analyticsHelper25 === void 0 || _this$analyticsHelper25.sendErrorEvent(error, 'Error while processing steps');
603
+ (_this$analyticsHelper26 = this.analyticsHelper) === null || _this$analyticsHelper26 === void 0 || _this$analyticsHelper26.sendErrorEvent(error, 'Error while processing steps');
590
604
  this.throttledCatchup();
591
605
  }
592
606
  }
@@ -616,8 +630,8 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
616
630
  var _this$getState7;
617
631
  var state = (_this$getState7 = this.getState) === null || _this$getState7 === void 0 ? void 0 : _this$getState7.call(this);
618
632
  if (!state) {
619
- var _this$analyticsHelper26;
620
- (_this$analyticsHelper26 = this.analyticsHelper) === null || _this$analyticsHelper26 === void 0 || _this$analyticsHelper26.sendErrorEvent(new Error('Editor state is undefined'), 'sendStepsFromCurrentState called without state');
633
+ var _this$analyticsHelper27;
634
+ (_this$analyticsHelper27 = this.analyticsHelper) === null || _this$analyticsHelper27 === void 0 || _this$analyticsHelper27.sendErrorEvent(new Error('Editor state is undefined'), 'sendStepsFromCurrentState called without state');
621
635
  return;
622
636
  }
623
637
  this.send(null, null, state, sendAnalyticsEvent);
@@ -642,8 +656,8 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
642
656
  // to ensure that analytics events with the number of unconfirmed steps is only
643
657
  // sent once on connection (as opposed to on every step)
644
658
  if (sendAnalyticsEvent) {
645
- var _this$analyticsHelper27;
646
- (_this$analyticsHelper27 = this.analyticsHelper) === null || _this$analyticsHelper27 === void 0 || _this$analyticsHelper27.sendActionEvent(_const.EVENT_ACTION.HAS_UNCONFIRMED_STEPS, _const.EVENT_STATUS.INFO, {
659
+ var _this$analyticsHelper28;
660
+ (_this$analyticsHelper28 = this.analyticsHelper) === null || _this$analyticsHelper28 === void 0 || _this$analyticsHelper28.sendActionEvent(_const.EVENT_ACTION.HAS_UNCONFIRMED_STEPS, _const.EVENT_STATUS.INFO, {
647
661
  numUnconfirmedSteps: (unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length) || 0
648
662
  });
649
663
  }
@@ -651,16 +665,15 @@ var DocumentService = exports.DocumentService = /*#__PURE__*/function () {
651
665
  return;
652
666
  }
653
667
  if (this.enableSendStepsQueue) {
654
- // This is where we would call the sendStepsQueue instead of throttledCommitStep
655
668
  // Only send 1% of events to avoid useless logging
656
669
  if (Math.random() < 0.01) {
657
- var _this$analyticsHelper28;
658
- (_this$analyticsHelper28 = this.analyticsHelper) === null || _this$analyticsHelper28 === void 0 || _this$analyticsHelper28.sendActionEvent(_const.EVENT_ACTION.SEND_STEPS_QUEUE, _const.EVENT_STATUS.INFO);
670
+ var _this$analyticsHelper29;
671
+ (_this$analyticsHelper29 = this.analyticsHelper) === null || _this$analyticsHelper29 === void 0 || _this$analyticsHelper29.sendActionEvent(_const.EVENT_ACTION.SEND_STEPS_QUEUE, _const.EVENT_STATUS.INFO);
659
672
  }
660
673
  // Avoid reference issues using a
661
674
  // method outside of the provider
662
675
  // scope
663
- (0, _commitStep.throttledCommitStep)({
676
+ (0, _commitStep.commitStepQueue)({
664
677
  broadcast: this.broadcast,
665
678
  userId: this.getUserId(),
666
679
  clientId: this.clientId,
@@ -10,6 +10,7 @@ var EVENT_ACTION = exports.EVENT_ACTION = /*#__PURE__*/function (EVENT_ACTION) {
10
10
  EVENT_ACTION["DOCUMENT_INIT"] = "documentInit";
11
11
  EVENT_ACTION["ADD_STEPS"] = "addSteps";
12
12
  EVENT_ACTION["UPDATE_PARTICIPANTS"] = "updateParticipants";
13
+ EVENT_ACTION["UPDATE_DOCUMENT"] = "updateDocument";
13
14
  EVENT_ACTION["COMMIT_UNCONFIRMED_STEPS"] = "commitUnconfirmedSteps";
14
15
  EVENT_ACTION["REINITIALISE_DOCUMENT"] = "reinitialiseDocument";
15
16
  EVENT_ACTION["ERROR"] = "error";
@@ -4,18 +4,20 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.throttledCommitStep = exports.commitStep = void 0;
7
+ exports.throttledCommitStep = exports.commitStepQueue = exports.commitStep = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _countBy = _interopRequireDefault(require("lodash/countBy"));
10
10
  var _throttle = _interopRequireDefault(require("lodash/throttle"));
11
11
  var _const = require("../helpers/const");
12
12
  var _types = require("../types");
13
13
  var _errorTypes = require("../errors/error-types");
14
+ var _utils = require("../helpers/utils");
14
15
  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; }
15
16
  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; }
16
17
  var SEND_STEPS_THROTTLE = 500; // 0.5 second
17
-
18
- var commitStep = exports.commitStep = function commitStep(_ref) {
18
+ var logger = (0, _utils.createLogger)('commit-step', 'black');
19
+ var readyToCommit = true;
20
+ var commitStepQueue = exports.commitStepQueue = function commitStepQueue(_ref) {
19
21
  var broadcast = _ref.broadcast,
20
22
  steps = _ref.steps,
21
23
  version = _ref.version,
@@ -25,6 +27,107 @@ var commitStep = exports.commitStep = function commitStep(_ref) {
25
27
  onErrorHandled = _ref.onErrorHandled,
26
28
  analyticsHelper = _ref.analyticsHelper,
27
29
  emit = _ref.emit;
30
+ if (!readyToCommit) {
31
+ logger('Not ready to commit, skip');
32
+ return;
33
+ }
34
+ // Block other sending request, before ACK
35
+ readyToCommit = false;
36
+ var timer = setTimeout(function () {
37
+ readyToCommit = true;
38
+ logger('reset readyToCommit by timer');
39
+ }, 5000);
40
+ var stepsWithClientAndUserId = steps.map(function (step) {
41
+ return _objectSpread(_objectSpread({}, step.toJSON()), {}, {
42
+ clientId: clientId,
43
+ userId: userId
44
+ });
45
+ });
46
+ var start = new Date().getTime();
47
+ emit('commit-status', {
48
+ status: 'attempt',
49
+ version: version
50
+ });
51
+ try {
52
+ broadcast('steps:commit', {
53
+ steps: stepsWithClientAndUserId,
54
+ version: version,
55
+ userId: userId
56
+ }, function (response) {
57
+ var latency = new Date().getTime() - start;
58
+ if (timer) {
59
+ clearTimeout(timer);
60
+ if (latency <= 400) {
61
+ setTimeout(function () {
62
+ readyToCommit = true;
63
+ logger('reset readyToCommit');
64
+ }, 100);
65
+ } else {
66
+ readyToCommit = true;
67
+ logger('reset readyToCommit');
68
+ }
69
+ }
70
+ if (response.type === _types.AcknowledgementResponseTypes.SUCCESS) {
71
+ onStepsAdded({
72
+ steps: stepsWithClientAndUserId,
73
+ version: response.version
74
+ });
75
+ // Sample only 10% of add steps events to avoid overwhelming the analytics
76
+ if (Math.random() < 0.1) {
77
+ analyticsHelper === null || analyticsHelper === void 0 || analyticsHelper.sendActionEvent(_const.EVENT_ACTION.ADD_STEPS, _const.EVENT_STATUS.SUCCESS_10x_SAMPLED, {
78
+ type: _const.ADD_STEPS_TYPE.ACCEPTED,
79
+ latency: latency,
80
+ stepType: (0, _countBy.default)(stepsWithClientAndUserId, function (stepWithClientAndUserId) {
81
+ return stepWithClientAndUserId.stepType;
82
+ })
83
+ });
84
+ }
85
+ emit('commit-status', {
86
+ status: 'success',
87
+ version: response.version
88
+ });
89
+ } else if (response.type === _types.AcknowledgementResponseTypes.ERROR) {
90
+ onErrorHandled(response.error);
91
+ analyticsHelper === null || analyticsHelper === void 0 || analyticsHelper.sendActionEvent(_const.EVENT_ACTION.ADD_STEPS, _const.EVENT_STATUS.FAILURE, {
92
+ // User tried committing steps but they were rejected because:
93
+ // - HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
94
+ // - VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
95
+ type: response.error.data.code === _errorTypes.NCS_ERROR_CODE.HEAD_VERSION_UPDATE_FAILED || response.error.data.code === _errorTypes.NCS_ERROR_CODE.VERSION_NUMBER_ALREADY_EXISTS ? _const.ADD_STEPS_TYPE.REJECTED : _const.ADD_STEPS_TYPE.ERROR,
96
+ latency: latency
97
+ });
98
+ analyticsHelper === null || analyticsHelper === void 0 || analyticsHelper.sendErrorEvent(response.error, 'Error while adding steps - Acknowledgement Error');
99
+ emit('commit-status', {
100
+ status: 'failure',
101
+ version: version
102
+ });
103
+ } else {
104
+ analyticsHelper === null || analyticsHelper === void 0 || analyticsHelper.sendErrorEvent(
105
+ // @ts-expect-error We didn't type the invalid type case
106
+ new Error("Response type: ".concat((response === null || response === void 0 ? void 0 : response.type) || 'No response type')), 'Error while adding steps - Invalid Acknowledgement');
107
+ emit('commit-status', {
108
+ status: 'failure',
109
+ version: version
110
+ });
111
+ }
112
+ });
113
+ } catch (error) {
114
+ analyticsHelper === null || analyticsHelper === void 0 || analyticsHelper.sendErrorEvent(error, 'Error while adding steps - Broadcast threw exception');
115
+ emit('commit-status', {
116
+ status: 'failure',
117
+ version: version
118
+ });
119
+ }
120
+ };
121
+ var commitStep = exports.commitStep = function commitStep(_ref2) {
122
+ var broadcast = _ref2.broadcast,
123
+ steps = _ref2.steps,
124
+ version = _ref2.version,
125
+ userId = _ref2.userId,
126
+ clientId = _ref2.clientId,
127
+ onStepsAdded = _ref2.onStepsAdded,
128
+ onErrorHandled = _ref2.onErrorHandled,
129
+ analyticsHelper = _ref2.analyticsHelper,
130
+ emit = _ref2.emit;
28
131
  var stepsWithClientAndUserId = steps.map(function (step) {
29
132
  return _objectSpread(_objectSpread({}, step.toJSON()), {}, {
30
133
  clientId: clientId,
@@ -274,6 +274,12 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
274
274
  return _this.userId;
275
275
  }, _this.onErrorHandled, _this.metadataService, _this.config.enableErrorOnFailedDocumentApply, (0, _featureFlags.getCollabProviderFeatureFlag)('sendStepsQueueFF', _this.config.featureFlags));
276
276
  _this.namespaceService = new _namespaceService.NamespaceService();
277
+ if ((0, _featureFlags.getCollabProviderFeatureFlag)('sendStepsQueueFF', _this.config.featureFlags)) {
278
+ _this.sendStepsTimer = setInterval(function () {
279
+ logger('Intervally sendStepsFromCurrentState');
280
+ _this.documentService.sendStepsFromCurrentState(true);
281
+ }, 5000);
282
+ }
277
283
  return _this;
278
284
  }
279
285
  (0, _createClass2.default)(Provider, [{
@@ -468,6 +474,12 @@ var Provider = exports.Provider = /*#__PURE__*/function (_Emitter) {
468
474
  try {
469
475
  (0, _get2.default)((0, _getPrototypeOf2.default)(Provider.prototype), "unsubscribeAll", this).call(this);
470
476
  this.channel.disconnect();
477
+ if ((0, _featureFlags.getCollabProviderFeatureFlag)('sendStepsQueueFF', this.config.featureFlags)) {
478
+ if (this.sendStepsTimer) {
479
+ clearInterval(this.sendStepsTimer);
480
+ this.sendStepsTimer = undefined;
481
+ }
482
+ }
471
483
  } catch (error) {
472
484
  var _this$analyticsHelper11;
473
485
  (_this$analyticsHelper11 = this.analyticsHelper) === null || _this$analyticsHelper11 === void 0 || _this$analyticsHelper11.sendErrorEvent(error, 'Error while shutting down the collab provider');
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = exports.nextMajorVersion = exports.name = void 0;
7
7
  var name = exports.name = "@atlaskit/collab-provider";
8
- var version = exports.version = "9.17.2";
8
+ var version = exports.version = "9.17.4";
9
9
  var nextMajorVersion = exports.nextMajorVersion = function nextMajorVersion() {
10
10
  return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
11
11
  };
@@ -3,12 +3,13 @@ import { ACK_MAX_TRY, EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
3
3
  import { getVersion, sendableSteps } from '@atlaskit/prosemirror-collab';
4
4
  import { createLogger, getStepUGCFreeDetails, sleep } from '../helpers/utils';
5
5
  import throttle from 'lodash/throttle';
6
- import { throttledCommitStep } from '../provider/commit-step';
6
+ import { throttledCommitStep, commitStepQueue } from '../provider/commit-step';
7
7
  import { MEASURE_NAME, startMeasure, stopMeasure } from '../analytics/performance';
8
8
  import { JSONTransformer } from '@atlaskit/editor-json-transformer';
9
9
  import { MAX_STEP_REJECTED_ERROR, MAX_STEP_REJECTED_ERROR_AGGRESSIVE } from '../provider';
10
10
  import { catchup } from './catchup';
11
11
  import { StepQueueState } from './step-queue-state';
12
+ import { getCollabProviderFeatureFlag } from '../feature-flags';
12
13
  import { CantSyncUpError, INTERNAL_ERROR_CODE, UpdateDocumentError } from '../errors/error-types';
13
14
  const CATCHUP_THROTTLE = 1 * 1000; // 1 second
14
15
 
@@ -30,8 +31,9 @@ export class DocumentService {
30
31
  * @param onErrorHandled - Callback to handle
31
32
  * @param metadataService
32
33
  * @param enableErrorOnFailedDocumentApply - Enable failed document update exceptions.
34
+ * @param enableSendStepsQueue - Enable send steps queue.
33
35
  */
34
- constructor(participantsService, analyticsHelper, fetchCatchup, fetchReconcile, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService, enableErrorOnFailedDocumentApply = false, enableSendStepsQueue = false) {
36
+ constructor(participantsService, analyticsHelper, fetchCatchup, fetchReconcile, providerEmitCallback, broadcast, getUserId, onErrorHandled, metadataService, enableErrorOnFailedDocumentApply = false, enableSendStepsQueue = getCollabProviderFeatureFlag('sendStepsQueueFF')) {
35
37
  // Fires analytics to editor when collab editor cannot sync up
36
38
  _defineProperty(this, "stepRejectCounter", 0);
37
39
  _defineProperty(this, "aggressiveCatchup", false);
@@ -82,7 +84,7 @@ export class DocumentService {
82
84
  } finally {
83
85
  this.stepQueue.resumeQueue();
84
86
  this.processQueue();
85
- this.sendStepsFromCurrentState(); // this will eventually retry catchup as it calls commitStep which will either catchup on onStepsAdded or onErrorHandled
87
+ this.sendStepsFromCurrentState(); // this will eventually retry catchup as it calls throttledCommitStep which will either catchup on onStepsAdded or onErrorHandled
86
88
  this.stepRejectCounter = 0;
87
89
  }
88
90
  });
@@ -292,10 +294,13 @@ export class DocumentService {
292
294
  reserveCursor
293
295
  } : {})
294
296
  });
297
+ this.updateDocumentAnalytics(doc, version);
298
+ });
299
+ _defineProperty(this, "updateDocumentAnalytics", (doc, version) => {
295
300
  const updatedVersion = this.getCurrentPmVersion();
301
+ const isDocContentValid = this.validatePMJSONDocument(doc);
296
302
  if (this.getCurrentPmVersion() !== version) {
297
303
  var _doc$content, _this$analyticsHelper17;
298
- const isDocContentValid = this.validatePMJSONDocument(doc);
299
304
  const error = new UpdateDocumentError('Failed to update the document', {
300
305
  newVersion: version,
301
306
  editorVersion: updatedVersion,
@@ -319,14 +324,23 @@ export class DocumentService {
319
324
  throw error;
320
325
  }
321
326
  // Otherwise just fail silently for now
327
+ } else {
328
+ var _this$analyticsHelper18, _doc$content2;
329
+ (_this$analyticsHelper18 = this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 ? void 0 : _this$analyticsHelper18.sendActionEvent(EVENT_ACTION.UPDATE_DOCUMENT, EVENT_STATUS.SUCCESS, {
330
+ newVersion: version,
331
+ editorVersion: updatedVersion,
332
+ isDocTruthy: !!doc,
333
+ docHasContent: (doc === null || doc === void 0 ? void 0 : (_doc$content2 = doc.content) === null || _doc$content2 === void 0 ? void 0 : _doc$content2.length) >= 1,
334
+ isDocContentValid
335
+ });
322
336
  }
323
337
  });
324
338
  _defineProperty(this, "validatePMJSONDocument", doc => {
325
339
  try {
326
340
  var _this$getState5;
327
341
  if (!((_this$getState5 = this.getState) !== null && _this$getState5 !== void 0 && _this$getState5.call(this))) {
328
- var _this$analyticsHelper18;
329
- (_this$analyticsHelper18 = this.analyticsHelper) === null || _this$analyticsHelper18 === void 0 ? void 0 : _this$analyticsHelper18.sendErrorEvent(new Error('Editor state is undefined'), 'validatePMJSONDocument called without state');
342
+ var _this$analyticsHelper19;
343
+ (_this$analyticsHelper19 = this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 ? void 0 : _this$analyticsHelper19.sendErrorEvent(new Error('Editor state is undefined'), 'validatePMJSONDocument called without state');
330
344
  }
331
345
  const state = this.getState();
332
346
  const content = (doc.content || []).map(child => state.schema.nodeFromJSON(child));
@@ -350,7 +364,7 @@ export class DocumentService {
350
364
  const unconfirmedSteps = this.getUnconfirmedSteps();
351
365
  try {
352
366
  if (unconfirmedSteps !== null && unconfirmedSteps !== void 0 && unconfirmedSteps.length) {
353
- var _this$getState6, _this$analyticsHelper20;
367
+ var _this$getState6, _this$analyticsHelper21;
354
368
  startMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
355
369
  let count = 0;
356
370
  // We use origins here as steps can be rebased. When steps are rebased a new step is created.
@@ -361,8 +375,8 @@ export class DocumentService {
361
375
  const lastTr = unconfirmedTrs === null || unconfirmedTrs === void 0 ? void 0 : unconfirmedTrs[unconfirmedTrs.length - 1];
362
376
  let isLastTrConfirmed = false;
363
377
  if (!((_this$getState6 = this.getState) !== null && _this$getState6 !== void 0 && _this$getState6.call(this))) {
364
- var _this$analyticsHelper19;
365
- (_this$analyticsHelper19 = this.analyticsHelper) === null || _this$analyticsHelper19 === void 0 ? void 0 : _this$analyticsHelper19.sendErrorEvent(new Error('Editor state is undefined'), 'commitUnconfirmedSteps called without state');
378
+ var _this$analyticsHelper20;
379
+ (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendErrorEvent(new Error('Editor state is undefined'), 'commitUnconfirmedSteps called without state');
366
380
  }
367
381
  while (!isLastTrConfirmed) {
368
382
  this.sendStepsFromCurrentState();
@@ -394,35 +408,35 @@ export class DocumentService {
394
408
  }
395
409
  }
396
410
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
397
- (_this$analyticsHelper20 = this.analyticsHelper) === null || _this$analyticsHelper20 === void 0 ? void 0 : _this$analyticsHelper20.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.SUCCESS, {
411
+ (_this$analyticsHelper21 = this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 ? void 0 : _this$analyticsHelper21.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.SUCCESS, {
398
412
  latency: measure === null || measure === void 0 ? void 0 : measure.duration,
399
413
  // upon success, emit the total number of unconfirmed steps we synced
400
414
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
401
415
  });
402
416
  }
403
417
  } catch (error) {
404
- var _this$analyticsHelper21, _this$analyticsHelper22;
418
+ var _this$analyticsHelper22, _this$analyticsHelper23;
405
419
  const measure = stopMeasure(MEASURE_NAME.COMMIT_UNCONFIRMED_STEPS, this.analyticsHelper);
406
- (_this$analyticsHelper21 = this.analyticsHelper) === null || _this$analyticsHelper21 === void 0 ? void 0 : _this$analyticsHelper21.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.FAILURE, {
420
+ (_this$analyticsHelper22 = this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 ? void 0 : _this$analyticsHelper22.sendActionEvent(EVENT_ACTION.COMMIT_UNCONFIRMED_STEPS, EVENT_STATUS.FAILURE, {
407
421
  latency: measure === null || measure === void 0 ? void 0 : measure.duration,
408
422
  numUnconfirmedSteps: unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length
409
423
  });
410
- (_this$analyticsHelper22 = this.analyticsHelper) === null || _this$analyticsHelper22 === void 0 ? void 0 : _this$analyticsHelper22.sendErrorEvent(error, 'Error while committing unconfirmed steps');
424
+ (_this$analyticsHelper23 = this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 ? void 0 : _this$analyticsHelper23.sendErrorEvent(error, 'Error while committing unconfirmed steps');
411
425
  throw error;
412
426
  }
413
427
  });
414
428
  _defineProperty(this, "onStepRejectedError", () => {
415
- var _this$analyticsHelper23;
429
+ var _this$analyticsHelper24;
416
430
  this.stepRejectCounter++;
417
431
  logger(`Steps rejected (tries=${this.stepRejectCounter})`);
418
- (_this$analyticsHelper23 = this.analyticsHelper) === null || _this$analyticsHelper23 === void 0 ? void 0 : _this$analyticsHelper23.sendActionEvent(EVENT_ACTION.SEND_STEPS_RETRY, EVENT_STATUS.INFO, {
432
+ (_this$analyticsHelper24 = this.analyticsHelper) === null || _this$analyticsHelper24 === void 0 ? void 0 : _this$analyticsHelper24.sendActionEvent(EVENT_ACTION.SEND_STEPS_RETRY, EVENT_STATUS.INFO, {
419
433
  count: this.stepRejectCounter
420
434
  });
421
435
  let maxRetries = this.aggressiveCatchup ? MAX_STEP_REJECTED_ERROR_AGGRESSIVE : MAX_STEP_REJECTED_ERROR;
422
436
  if (this.stepRejectCounter >= maxRetries) {
423
- var _this$analyticsHelper24;
437
+ var _this$analyticsHelper25;
424
438
  logger(`The steps were rejected too many times (tries=${this.stepRejectCounter}, limit=${MAX_STEP_REJECTED_ERROR}). Trying to catch-up.`);
425
- (_this$analyticsHelper24 = this.analyticsHelper) === null || _this$analyticsHelper24 === void 0 ? void 0 : _this$analyticsHelper24.sendActionEvent(EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, EVENT_STATUS.INFO);
439
+ (_this$analyticsHelper25 = this.analyticsHelper) === null || _this$analyticsHelper25 === void 0 ? void 0 : _this$analyticsHelper25.sendActionEvent(EVENT_ACTION.CATCHUP_AFTER_MAX_SEND_STEPS_RETRY, EVENT_STATUS.INFO);
426
440
  this.throttledCatchup();
427
441
  } else {
428
442
  // If committing steps failed try again automatically in 1s
@@ -488,9 +502,9 @@ export class DocumentService {
488
502
  setTimeout(() => this.sendStepsFromCurrentState(), 100);
489
503
  }
490
504
  } catch (error) {
491
- var _this$analyticsHelper25;
505
+ var _this$analyticsHelper26;
492
506
  logger(`Processing steps failed with error: ${error}. Triggering catch up call.`);
493
- (_this$analyticsHelper25 = this.analyticsHelper) === null || _this$analyticsHelper25 === void 0 ? void 0 : _this$analyticsHelper25.sendErrorEvent(error, 'Error while processing steps');
507
+ (_this$analyticsHelper26 = this.analyticsHelper) === null || _this$analyticsHelper26 === void 0 ? void 0 : _this$analyticsHelper26.sendErrorEvent(error, 'Error while processing steps');
494
508
  this.throttledCatchup();
495
509
  }
496
510
  }
@@ -517,8 +531,8 @@ export class DocumentService {
517
531
  var _this$getState7;
518
532
  const state = (_this$getState7 = this.getState) === null || _this$getState7 === void 0 ? void 0 : _this$getState7.call(this);
519
533
  if (!state) {
520
- var _this$analyticsHelper26;
521
- (_this$analyticsHelper26 = this.analyticsHelper) === null || _this$analyticsHelper26 === void 0 ? void 0 : _this$analyticsHelper26.sendErrorEvent(new Error('Editor state is undefined'), 'sendStepsFromCurrentState called without state');
534
+ var _this$analyticsHelper27;
535
+ (_this$analyticsHelper27 = this.analyticsHelper) === null || _this$analyticsHelper27 === void 0 ? void 0 : _this$analyticsHelper27.sendErrorEvent(new Error('Editor state is undefined'), 'sendStepsFromCurrentState called without state');
522
536
  return;
523
537
  }
524
538
  this.send(null, null, state, sendAnalyticsEvent);
@@ -540,8 +554,8 @@ export class DocumentService {
540
554
  // to ensure that analytics events with the number of unconfirmed steps is only
541
555
  // sent once on connection (as opposed to on every step)
542
556
  if (sendAnalyticsEvent) {
543
- var _this$analyticsHelper27;
544
- (_this$analyticsHelper27 = this.analyticsHelper) === null || _this$analyticsHelper27 === void 0 ? void 0 : _this$analyticsHelper27.sendActionEvent(EVENT_ACTION.HAS_UNCONFIRMED_STEPS, EVENT_STATUS.INFO, {
557
+ var _this$analyticsHelper28;
558
+ (_this$analyticsHelper28 = this.analyticsHelper) === null || _this$analyticsHelper28 === void 0 ? void 0 : _this$analyticsHelper28.sendActionEvent(EVENT_ACTION.HAS_UNCONFIRMED_STEPS, EVENT_STATUS.INFO, {
545
559
  numUnconfirmedSteps: (unconfirmedSteps === null || unconfirmedSteps === void 0 ? void 0 : unconfirmedSteps.length) || 0
546
560
  });
547
561
  }
@@ -549,16 +563,15 @@ export class DocumentService {
549
563
  return;
550
564
  }
551
565
  if (this.enableSendStepsQueue) {
552
- // This is where we would call the sendStepsQueue instead of throttledCommitStep
553
566
  // Only send 1% of events to avoid useless logging
554
567
  if (Math.random() < 0.01) {
555
- var _this$analyticsHelper28;
556
- (_this$analyticsHelper28 = this.analyticsHelper) === null || _this$analyticsHelper28 === void 0 ? void 0 : _this$analyticsHelper28.sendActionEvent(EVENT_ACTION.SEND_STEPS_QUEUE, EVENT_STATUS.INFO);
568
+ var _this$analyticsHelper29;
569
+ (_this$analyticsHelper29 = this.analyticsHelper) === null || _this$analyticsHelper29 === void 0 ? void 0 : _this$analyticsHelper29.sendActionEvent(EVENT_ACTION.SEND_STEPS_QUEUE, EVENT_STATUS.INFO);
557
570
  }
558
571
  // Avoid reference issues using a
559
572
  // method outside of the provider
560
573
  // scope
561
- throttledCommitStep({
574
+ commitStepQueue({
562
575
  broadcast: this.broadcast,
563
576
  userId: this.getUserId(),
564
577
  clientId: this.clientId,
@@ -4,6 +4,7 @@ export let EVENT_ACTION = /*#__PURE__*/function (EVENT_ACTION) {
4
4
  EVENT_ACTION["DOCUMENT_INIT"] = "documentInit";
5
5
  EVENT_ACTION["ADD_STEPS"] = "addSteps";
6
6
  EVENT_ACTION["UPDATE_PARTICIPANTS"] = "updateParticipants";
7
+ EVENT_ACTION["UPDATE_DOCUMENT"] = "updateDocument";
7
8
  EVENT_ACTION["COMMIT_UNCONFIRMED_STEPS"] = "commitUnconfirmedSteps";
8
9
  EVENT_ACTION["REINITIALISE_DOCUMENT"] = "reinitialiseDocument";
9
10
  EVENT_ACTION["ERROR"] = "error";