@atlaskit/editor-synced-block-provider 2.12.2 → 2.12.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 2.12.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`707c3960baedb`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/707c3960baedb) -
8
+ EDITOR-1923 retry api requests on rate limited for sync block provider
9
+ - Updated dependencies
10
+
3
11
  ## 2.12.2
4
12
 
5
13
  ### Patch Changes
@@ -13,6 +13,7 @@ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/ge
13
13
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
14
14
  var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
15
15
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
16
+ var _retry = require("../../utils/retry");
16
17
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
17
18
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
18
19
  var isBlockContentResponse = exports.isBlockContentResponse = function isBlockContentResponse(response) {
@@ -69,7 +70,7 @@ var getReferenceSyncedBlocks = exports.getReferenceSyncedBlocks = /*#__PURE__*/f
69
70
  while (1) switch (_context.prev = _context.next) {
70
71
  case 0:
71
72
  _context.next = 2;
72
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/document/reference/").concat(encodeURIComponent(documentAri)), {
73
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/document/reference/").concat(encodeURIComponent(documentAri)), {
73
74
  method: 'GET',
74
75
  headers: COMMON_HEADERS
75
76
  });
@@ -119,7 +120,7 @@ var getSyncedBlockContent = exports.getSyncedBlockContent = /*#__PURE__*/functio
119
120
  case 0:
120
121
  blockAri = _ref2.blockAri;
121
122
  _context2.next = 3;
122
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
123
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
123
124
  method: 'GET',
124
125
  headers: COMMON_HEADERS
125
126
  });
@@ -153,7 +154,7 @@ var deleteSyncedBlock = exports.deleteSyncedBlock = /*#__PURE__*/function () {
153
154
  case 0:
154
155
  blockAri = _ref4.blockAri;
155
156
  _context3.next = 3;
156
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
157
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
157
158
  method: 'DELETE',
158
159
  headers: COMMON_HEADERS
159
160
  });
@@ -182,7 +183,7 @@ var updateSyncedBlock = exports.updateSyncedBlock = /*#__PURE__*/function () {
182
183
  case 0:
183
184
  blockAri = _ref6.blockAri, content = _ref6.content;
184
185
  _context4.next = 3;
185
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
186
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
186
187
  method: 'PUT',
187
188
  headers: COMMON_HEADERS,
188
189
  body: JSON.stringify({
@@ -214,7 +215,7 @@ var createSyncedBlock = exports.createSyncedBlock = /*#__PURE__*/function () {
214
215
  case 0:
215
216
  blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content;
216
217
  _context5.next = 3;
217
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block"), {
218
+ return (0, _retry.fetchWithRetry)("".concat(BLOCK_SERVICE_API_URL, "/block"), {
218
219
  method: 'POST',
219
220
  headers: COMMON_HEADERS,
220
221
  body: JSON.stringify({
@@ -8,6 +8,7 @@ exports.updateContentProperty = exports.getContentProperty = exports.deleteConte
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
+ var _retry = require("../../utils/retry");
11
12
  var _ari = require("./ari");
12
13
  var _utils = require("./utils");
13
14
  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; }
@@ -95,7 +96,7 @@ var getContentProperty = exports.getContentProperty = /*#__PURE__*/function () {
95
96
  }
96
97
  };
97
98
  _context.next = 6;
98
- return fetch(GRAPHQL_ENDPOINT, {
99
+ return (0, _retry.fetchWithRetry)(GRAPHQL_ENDPOINT, {
99
100
  method: 'POST',
100
101
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
101
102
  body: JSON.stringify(bodyData)
@@ -204,7 +205,7 @@ var createContentProperty = exports.createContentProperty = /*#__PURE__*/functio
204
205
  }
205
206
  };
206
207
  _context3.next = 6;
207
- return fetch(GRAPHQL_ENDPOINT, {
208
+ return (0, _retry.fetchWithRetry)(GRAPHQL_ENDPOINT, {
208
209
  method: 'POST',
209
210
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
210
211
  body: JSON.stringify(bodyData)
@@ -254,7 +255,7 @@ var deleteContentProperty = exports.deleteContentProperty = /*#__PURE__*/functio
254
255
  }
255
256
  };
256
257
  _context4.next = 6;
257
- return fetch(GRAPHQL_ENDPOINT, {
258
+ return (0, _retry.fetchWithRetry)(GRAPHQL_ENDPOINT, {
258
259
  method: 'POST',
259
260
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
260
261
  body: JSON.stringify(bodyData)
@@ -10,6 +10,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
10
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
11
  var _monitoring = require("@atlaskit/editor-common/monitoring");
12
12
  var _errorHandling = require("../../utils/errorHandling");
13
+ var _retry = require("../../utils/retry");
13
14
  var _ari = require("./ari");
14
15
  var _utils = require("./utils");
15
16
  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; }
@@ -43,7 +44,7 @@ var getConfluenceSourceInfo = /*#__PURE__*/function () {
43
44
  }
44
45
  };
45
46
  _context.next = 3;
46
- return fetch(GRAPHQL_ENDPOINT, {
47
+ return (0, _retry.fetchWithRetry)(GRAPHQL_ENDPOINT, {
47
48
  method: 'POST',
48
49
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
49
50
  body: JSON.stringify(bodyData)
@@ -8,6 +8,11 @@ var SyncBlockError = exports.SyncBlockError = /*#__PURE__*/function (SyncBlockEr
8
8
  SyncBlockError["Errored"] = "errored";
9
9
  SyncBlockError["NotFound"] = "not_found";
10
10
  SyncBlockError["Forbidden"] = "forbidden";
11
+ SyncBlockError["InvalidRequest"] = "invalid_request";
12
+ SyncBlockError["RateLimited"] = "rate_limited";
13
+ SyncBlockError["Conflict"] = "conflict";
14
+ // attempt to create block that already exists
15
+ SyncBlockError["ServerError"] = "server_error";
11
16
  SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
12
17
  return SyncBlockError;
13
18
  }({});
@@ -17,10 +17,21 @@ var _types = require("../../common/types");
17
17
  var _errorHandling = require("../../utils/errorHandling");
18
18
  var mapBlockError = function mapBlockError(error) {
19
19
  switch (error.status) {
20
+ case 400:
21
+ case 401:
22
+ return _types.SyncBlockError.InvalidRequest;
20
23
  case 403:
21
24
  return _types.SyncBlockError.Forbidden;
22
25
  case 404:
23
26
  return _types.SyncBlockError.NotFound;
27
+ case 409:
28
+ return _types.SyncBlockError.Conflict;
29
+ case 429:
30
+ return _types.SyncBlockError.RateLimited;
31
+ case 500:
32
+ case 503:
33
+ case 504:
34
+ return _types.SyncBlockError.ServerError;
24
35
  }
25
36
  return _types.SyncBlockError.Errored;
26
37
  };
@@ -26,10 +26,10 @@ var resolveSyncBlockInstance = exports.resolveSyncBlockInstance = function resol
26
26
  return newResult;
27
27
  } else if (!newResult.data) {
28
28
  // return the old result if there was an error, e.g. network error, but not if not found or forbidden
29
- if (newResult.error === _types.SyncBlockError.Errored) {
30
- return oldResult;
31
- } else {
29
+ if (newResult.error === _types.SyncBlockError.NotFound || newResult.error === _types.SyncBlockError.Forbidden) {
32
30
  return newResult;
31
+ } else {
32
+ return oldResult;
33
33
  }
34
34
  }
35
35
 
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.fetchWithRetry = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
+ var parseRetryAfter = function parseRetryAfter(retryAfter) {
11
+ var newDelay;
12
+
13
+ // retryAfter can either be in ms or HTTP date
14
+ var parsedRetryAfter = parseInt(retryAfter);
15
+ if (!isNaN(parsedRetryAfter)) {
16
+ newDelay = parsedRetryAfter * 1000;
17
+ } else {
18
+ var retryDate = new Date(retryAfter);
19
+ var delayFromDate = retryDate.getTime() - Date.now();
20
+ if (delayFromDate > 0) {
21
+ newDelay = delayFromDate;
22
+ }
23
+ }
24
+ return newDelay;
25
+ };
26
+ var _fetchWithRetry = exports.fetchWithRetry = /*#__PURE__*/function () {
27
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(url, options) {
28
+ var retriesRemaining,
29
+ delay,
30
+ response,
31
+ shouldRetry,
32
+ retryAfter,
33
+ _args = arguments;
34
+ return _regenerator.default.wrap(function _callee$(_context) {
35
+ while (1) switch (_context.prev = _context.next) {
36
+ case 0:
37
+ retriesRemaining = _args.length > 2 && _args[2] !== undefined ? _args[2] : 3;
38
+ delay = _args.length > 3 && _args[3] !== undefined ? _args[3] : 1000;
39
+ _context.next = 4;
40
+ return fetch(url, options);
41
+ case 4:
42
+ response = _context.sent;
43
+ shouldRetry = !response.ok && response.status === 429 && retriesRemaining > 1;
44
+ if (shouldRetry) {
45
+ _context.next = 8;
46
+ break;
47
+ }
48
+ return _context.abrupt("return", response);
49
+ case 8:
50
+ retryAfter = response.headers.get('Retry-After');
51
+ _context.next = 11;
52
+ return new Promise(function (resolve) {
53
+ return setTimeout(resolve, retryAfter ? parseRetryAfter(retryAfter) : delay);
54
+ });
55
+ case 11:
56
+ return _context.abrupt("return", _fetchWithRetry(url, options, retriesRemaining - 1, delay * 2));
57
+ case 12:
58
+ case "end":
59
+ return _context.stop();
60
+ }
61
+ }, _callee);
62
+ }));
63
+ return function fetchWithRetry(_x, _x2) {
64
+ return _ref.apply(this, arguments);
65
+ };
66
+ }();
@@ -1,3 +1,4 @@
1
+ import { fetchWithRetry } from '../../utils/retry';
1
2
  export const isBlockContentResponse = response => {
2
3
  const content = response.content;
3
4
  return typeof content === 'string';
@@ -46,7 +47,7 @@ export const isBlockContentResponse = response => {
46
47
  * Check https://block-service.dev.atl-paas.net/ for latest API documentation.
47
48
  */
48
49
  export const getReferenceSyncedBlocks = async documentAri => {
49
- const response = await fetch(`${BLOCK_SERVICE_API_URL}/block/document/reference/${encodeURIComponent(documentAri)}`, {
50
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/document/reference/${encodeURIComponent(documentAri)}`, {
50
51
  method: 'GET',
51
52
  headers: COMMON_HEADERS
52
53
  });
@@ -69,7 +70,7 @@ export class BlockError extends Error {
69
70
  export const getSyncedBlockContent = async ({
70
71
  blockAri
71
72
  }) => {
72
- const response = await fetch(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
73
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
73
74
  method: 'GET',
74
75
  headers: COMMON_HEADERS
75
76
  });
@@ -81,7 +82,7 @@ export const getSyncedBlockContent = async ({
81
82
  export const deleteSyncedBlock = async ({
82
83
  blockAri
83
84
  }) => {
84
- const response = await fetch(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
85
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
85
86
  method: 'DELETE',
86
87
  headers: COMMON_HEADERS
87
88
  });
@@ -93,7 +94,7 @@ export const updateSyncedBlock = async ({
93
94
  blockAri,
94
95
  content
95
96
  }) => {
96
- const response = await fetch(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
97
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block/${encodeURIComponent(blockAri)}`, {
97
98
  method: 'PUT',
98
99
  headers: COMMON_HEADERS,
99
100
  body: JSON.stringify({
@@ -111,7 +112,7 @@ export const createSyncedBlock = async ({
111
112
  product,
112
113
  content
113
114
  }) => {
114
- const response = await fetch(`${BLOCK_SERVICE_API_URL}/block`, {
115
+ const response = await fetchWithRetry(`${BLOCK_SERVICE_API_URL}/block`, {
115
116
  method: 'POST',
116
117
  headers: COMMON_HEADERS,
117
118
  body: JSON.stringify({
@@ -1,3 +1,4 @@
1
+ import { fetchWithRetry } from '../../utils/retry';
1
2
  import { getConfluencePageAri } from './ari';
2
3
  import { isBlogPageType } from './utils';
3
4
  const COMMON_HEADERS = {
@@ -151,7 +152,7 @@ export const getContentProperty = async ({
151
152
  keys: [key]
152
153
  }
153
154
  };
154
- const response = await fetch(GRAPHQL_ENDPOINT, {
155
+ const response = await fetchWithRetry(GRAPHQL_ENDPOINT, {
155
156
  method: 'POST',
156
157
  headers: {
157
158
  ...COMMON_HEADERS,
@@ -237,7 +238,7 @@ export const createContentProperty = async ({
237
238
  }
238
239
  }
239
240
  };
240
- const response = await fetch(GRAPHQL_ENDPOINT, {
241
+ const response = await fetchWithRetry(GRAPHQL_ENDPOINT, {
241
242
  method: 'POST',
242
243
  headers: {
243
244
  ...COMMON_HEADERS,
@@ -272,7 +273,7 @@ export const deleteContentProperty = async ({
272
273
  }
273
274
  }
274
275
  };
275
- const response = await fetch(GRAPHQL_ENDPOINT, {
276
+ const response = await fetchWithRetry(GRAPHQL_ENDPOINT, {
276
277
  method: 'POST',
277
278
  headers: {
278
279
  ...COMMON_HEADERS,
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { logException } from '@atlaskit/editor-common/monitoring';
4
4
  import { getSourceInfoErrorPayload } from '../../utils/errorHandling';
5
+ import { fetchWithRetry } from '../../utils/retry';
5
6
  import { getPageIdAndTypeFromConfluencePageAri } from './ari';
6
7
  import { isBlogPageType } from './utils';
7
8
  const COMMON_HEADERS = {
@@ -41,7 +42,7 @@ const getConfluenceSourceInfo = async ari => {
41
42
  id: ari
42
43
  }
43
44
  };
44
- const response = await fetch(GRAPHQL_ENDPOINT, {
45
+ const response = await fetchWithRetry(GRAPHQL_ENDPOINT, {
45
46
  method: 'POST',
46
47
  headers: {
47
48
  ...COMMON_HEADERS,
@@ -2,6 +2,11 @@ export let SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
2
2
  SyncBlockError["Errored"] = "errored";
3
3
  SyncBlockError["NotFound"] = "not_found";
4
4
  SyncBlockError["Forbidden"] = "forbidden";
5
+ SyncBlockError["InvalidRequest"] = "invalid_request";
6
+ SyncBlockError["RateLimited"] = "rate_limited";
7
+ SyncBlockError["Conflict"] = "conflict";
8
+ // attempt to create block that already exists
9
+ SyncBlockError["ServerError"] = "server_error";
5
10
  SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
6
11
  return SyncBlockError;
7
12
  }({});
@@ -5,10 +5,21 @@ import { SyncBlockError } from '../../common/types';
5
5
  import { stringifyError } from '../../utils/errorHandling';
6
6
  const mapBlockError = error => {
7
7
  switch (error.status) {
8
+ case 400:
9
+ case 401:
10
+ return SyncBlockError.InvalidRequest;
8
11
  case 403:
9
12
  return SyncBlockError.Forbidden;
10
13
  case 404:
11
14
  return SyncBlockError.NotFound;
15
+ case 409:
16
+ return SyncBlockError.Conflict;
17
+ case 429:
18
+ return SyncBlockError.RateLimited;
19
+ case 500:
20
+ case 503:
21
+ case 504:
22
+ return SyncBlockError.ServerError;
12
23
  }
13
24
  return SyncBlockError.Errored;
14
25
  };
@@ -16,10 +16,10 @@ export const resolveSyncBlockInstance = (oldResult, newResult) => {
16
16
  return newResult;
17
17
  } else if (!newResult.data) {
18
18
  // return the old result if there was an error, e.g. network error, but not if not found or forbidden
19
- if (newResult.error === SyncBlockError.Errored) {
20
- return oldResult;
21
- } else {
19
+ if (newResult.error === SyncBlockError.NotFound || newResult.error === SyncBlockError.Forbidden) {
22
20
  return newResult;
21
+ } else {
22
+ return oldResult;
23
23
  }
24
24
  }
25
25
 
@@ -0,0 +1,26 @@
1
+ const parseRetryAfter = retryAfter => {
2
+ let newDelay;
3
+
4
+ // retryAfter can either be in ms or HTTP date
5
+ const parsedRetryAfter = parseInt(retryAfter);
6
+ if (!isNaN(parsedRetryAfter)) {
7
+ newDelay = parsedRetryAfter * 1000;
8
+ } else {
9
+ const retryDate = new Date(retryAfter);
10
+ const delayFromDate = retryDate.getTime() - Date.now();
11
+ if (delayFromDate > 0) {
12
+ newDelay = delayFromDate;
13
+ }
14
+ }
15
+ return newDelay;
16
+ };
17
+ export const fetchWithRetry = async (url, options, retriesRemaining = 3, delay = 1000) => {
18
+ const response = await fetch(url, options);
19
+ const shouldRetry = !response.ok && response.status === 429 && retriesRemaining > 1;
20
+ if (!shouldRetry) {
21
+ return response;
22
+ }
23
+ const retryAfter = response.headers.get('Retry-After');
24
+ await new Promise(resolve => setTimeout(resolve, retryAfter ? parseRetryAfter(retryAfter) : delay));
25
+ return fetchWithRetry(url, options, retriesRemaining - 1, delay * 2);
26
+ };
@@ -8,6 +8,7 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
8
8
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
9
9
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
10
10
  import _regeneratorRuntime from "@babel/runtime/regenerator";
11
+ import { fetchWithRetry } from '../../utils/retry';
11
12
  export var isBlockContentResponse = function isBlockContentResponse(response) {
12
13
  var content = response.content;
13
14
  return typeof content === 'string';
@@ -62,7 +63,7 @@ export var getReferenceSyncedBlocks = /*#__PURE__*/function () {
62
63
  while (1) switch (_context.prev = _context.next) {
63
64
  case 0:
64
65
  _context.next = 2;
65
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/document/reference/").concat(encodeURIComponent(documentAri)), {
66
+ return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block/document/reference/").concat(encodeURIComponent(documentAri)), {
66
67
  method: 'GET',
67
68
  headers: COMMON_HEADERS
68
69
  });
@@ -112,7 +113,7 @@ export var getSyncedBlockContent = /*#__PURE__*/function () {
112
113
  case 0:
113
114
  blockAri = _ref2.blockAri;
114
115
  _context2.next = 3;
115
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
116
+ return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
116
117
  method: 'GET',
117
118
  headers: COMMON_HEADERS
118
119
  });
@@ -146,7 +147,7 @@ export var deleteSyncedBlock = /*#__PURE__*/function () {
146
147
  case 0:
147
148
  blockAri = _ref4.blockAri;
148
149
  _context3.next = 3;
149
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
150
+ return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
150
151
  method: 'DELETE',
151
152
  headers: COMMON_HEADERS
152
153
  });
@@ -175,7 +176,7 @@ export var updateSyncedBlock = /*#__PURE__*/function () {
175
176
  case 0:
176
177
  blockAri = _ref6.blockAri, content = _ref6.content;
177
178
  _context4.next = 3;
178
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
179
+ return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block/").concat(encodeURIComponent(blockAri)), {
179
180
  method: 'PUT',
180
181
  headers: COMMON_HEADERS,
181
182
  body: JSON.stringify({
@@ -207,7 +208,7 @@ export var createSyncedBlock = /*#__PURE__*/function () {
207
208
  case 0:
208
209
  blockAri = _ref8.blockAri, blockInstanceId = _ref8.blockInstanceId, sourceAri = _ref8.sourceAri, product = _ref8.product, content = _ref8.content;
209
210
  _context5.next = 3;
210
- return fetch("".concat(BLOCK_SERVICE_API_URL, "/block"), {
211
+ return fetchWithRetry("".concat(BLOCK_SERVICE_API_URL, "/block"), {
211
212
  method: 'POST',
212
213
  headers: COMMON_HEADERS,
213
214
  body: JSON.stringify({
@@ -3,6 +3,7 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
3
  import _regeneratorRuntime from "@babel/runtime/regenerator";
4
4
  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; }
5
5
  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) { _defineProperty(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; }
6
+ import { fetchWithRetry } from '../../utils/retry';
6
7
  import { getConfluencePageAri } from './ari';
7
8
  import { isBlogPageType } from './utils';
8
9
  var COMMON_HEADERS = {
@@ -88,7 +89,7 @@ export var getContentProperty = /*#__PURE__*/function () {
88
89
  }
89
90
  };
90
91
  _context.next = 6;
91
- return fetch(GRAPHQL_ENDPOINT, {
92
+ return fetchWithRetry(GRAPHQL_ENDPOINT, {
92
93
  method: 'POST',
93
94
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
94
95
  body: JSON.stringify(bodyData)
@@ -197,7 +198,7 @@ export var createContentProperty = /*#__PURE__*/function () {
197
198
  }
198
199
  };
199
200
  _context3.next = 6;
200
- return fetch(GRAPHQL_ENDPOINT, {
201
+ return fetchWithRetry(GRAPHQL_ENDPOINT, {
201
202
  method: 'POST',
202
203
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
203
204
  body: JSON.stringify(bodyData)
@@ -247,7 +248,7 @@ export var deleteContentProperty = /*#__PURE__*/function () {
247
248
  }
248
249
  };
249
250
  _context4.next = 6;
250
- return fetch(GRAPHQL_ENDPOINT, {
251
+ return fetchWithRetry(GRAPHQL_ENDPOINT, {
251
252
  method: 'POST',
252
253
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
253
254
  body: JSON.stringify(bodyData)
@@ -7,6 +7,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
7
7
 
8
8
  import { logException } from '@atlaskit/editor-common/monitoring';
9
9
  import { getSourceInfoErrorPayload } from '../../utils/errorHandling';
10
+ import { fetchWithRetry } from '../../utils/retry';
10
11
  import { getPageIdAndTypeFromConfluencePageAri } from './ari';
11
12
  import { isBlogPageType } from './utils';
12
13
  var COMMON_HEADERS = {
@@ -38,7 +39,7 @@ var getConfluenceSourceInfo = /*#__PURE__*/function () {
38
39
  }
39
40
  };
40
41
  _context.next = 3;
41
- return fetch(GRAPHQL_ENDPOINT, {
42
+ return fetchWithRetry(GRAPHQL_ENDPOINT, {
42
43
  method: 'POST',
43
44
  headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
44
45
  body: JSON.stringify(bodyData)
@@ -2,6 +2,11 @@ export var SyncBlockError = /*#__PURE__*/function (SyncBlockError) {
2
2
  SyncBlockError["Errored"] = "errored";
3
3
  SyncBlockError["NotFound"] = "not_found";
4
4
  SyncBlockError["Forbidden"] = "forbidden";
5
+ SyncBlockError["InvalidRequest"] = "invalid_request";
6
+ SyncBlockError["RateLimited"] = "rate_limited";
7
+ SyncBlockError["Conflict"] = "conflict";
8
+ // attempt to create block that already exists
9
+ SyncBlockError["ServerError"] = "server_error";
5
10
  SyncBlockError["InvalidContent"] = "invalid_content"; // content is not a valid JSON
6
11
  return SyncBlockError;
7
12
  }({});
@@ -10,10 +10,21 @@ import { SyncBlockError } from '../../common/types';
10
10
  import { stringifyError } from '../../utils/errorHandling';
11
11
  var mapBlockError = function mapBlockError(error) {
12
12
  switch (error.status) {
13
+ case 400:
14
+ case 401:
15
+ return SyncBlockError.InvalidRequest;
13
16
  case 403:
14
17
  return SyncBlockError.Forbidden;
15
18
  case 404:
16
19
  return SyncBlockError.NotFound;
20
+ case 409:
21
+ return SyncBlockError.Conflict;
22
+ case 429:
23
+ return SyncBlockError.RateLimited;
24
+ case 500:
25
+ case 503:
26
+ case 504:
27
+ return SyncBlockError.ServerError;
17
28
  }
18
29
  return SyncBlockError.Errored;
19
30
  };
@@ -19,10 +19,10 @@ export var resolveSyncBlockInstance = function resolveSyncBlockInstance(oldResul
19
19
  return newResult;
20
20
  } else if (!newResult.data) {
21
21
  // return the old result if there was an error, e.g. network error, but not if not found or forbidden
22
- if (newResult.error === SyncBlockError.Errored) {
23
- return oldResult;
24
- } else {
22
+ if (newResult.error === SyncBlockError.NotFound || newResult.error === SyncBlockError.Forbidden) {
25
23
  return newResult;
24
+ } else {
25
+ return oldResult;
26
26
  }
27
27
  }
28
28
 
@@ -0,0 +1,60 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ var parseRetryAfter = function parseRetryAfter(retryAfter) {
4
+ var newDelay;
5
+
6
+ // retryAfter can either be in ms or HTTP date
7
+ var parsedRetryAfter = parseInt(retryAfter);
8
+ if (!isNaN(parsedRetryAfter)) {
9
+ newDelay = parsedRetryAfter * 1000;
10
+ } else {
11
+ var retryDate = new Date(retryAfter);
12
+ var delayFromDate = retryDate.getTime() - Date.now();
13
+ if (delayFromDate > 0) {
14
+ newDelay = delayFromDate;
15
+ }
16
+ }
17
+ return newDelay;
18
+ };
19
+ var _fetchWithRetry = /*#__PURE__*/function () {
20
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(url, options) {
21
+ var retriesRemaining,
22
+ delay,
23
+ response,
24
+ shouldRetry,
25
+ retryAfter,
26
+ _args = arguments;
27
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
28
+ while (1) switch (_context.prev = _context.next) {
29
+ case 0:
30
+ retriesRemaining = _args.length > 2 && _args[2] !== undefined ? _args[2] : 3;
31
+ delay = _args.length > 3 && _args[3] !== undefined ? _args[3] : 1000;
32
+ _context.next = 4;
33
+ return fetch(url, options);
34
+ case 4:
35
+ response = _context.sent;
36
+ shouldRetry = !response.ok && response.status === 429 && retriesRemaining > 1;
37
+ if (shouldRetry) {
38
+ _context.next = 8;
39
+ break;
40
+ }
41
+ return _context.abrupt("return", response);
42
+ case 8:
43
+ retryAfter = response.headers.get('Retry-After');
44
+ _context.next = 11;
45
+ return new Promise(function (resolve) {
46
+ return setTimeout(resolve, retryAfter ? parseRetryAfter(retryAfter) : delay);
47
+ });
48
+ case 11:
49
+ return _context.abrupt("return", _fetchWithRetry(url, options, retriesRemaining - 1, delay * 2));
50
+ case 12:
51
+ case "end":
52
+ return _context.stop();
53
+ }
54
+ }, _callee);
55
+ }));
56
+ return function fetchWithRetry(_x, _x2) {
57
+ return _ref.apply(this, arguments);
58
+ };
59
+ }();
60
+ export { _fetchWithRetry as fetchWithRetry };
@@ -16,6 +16,10 @@ export declare enum SyncBlockError {
16
16
  Errored = "errored",
17
17
  NotFound = "not_found",
18
18
  Forbidden = "forbidden",
19
+ InvalidRequest = "invalid_request",
20
+ RateLimited = "rate_limited",
21
+ Conflict = "conflict",// attempt to create block that already exists
22
+ ServerError = "server_error",
19
23
  InvalidContent = "invalid_content"
20
24
  }
21
25
  export interface SyncBlockData {
@@ -0,0 +1 @@
1
+ export declare const fetchWithRetry: (url: string, options: RequestInit, retriesRemaining?: number, delay?: number) => Promise<Response>;
@@ -16,6 +16,10 @@ export declare enum SyncBlockError {
16
16
  Errored = "errored",
17
17
  NotFound = "not_found",
18
18
  Forbidden = "forbidden",
19
+ InvalidRequest = "invalid_request",
20
+ RateLimited = "rate_limited",
21
+ Conflict = "conflict",// attempt to create block that already exists
22
+ ServerError = "server_error",
19
23
  InvalidContent = "invalid_content"
20
24
  }
21
25
  export interface SyncBlockData {
@@ -0,0 +1 @@
1
+ export declare const fetchWithRetry: (url: string, options: RequestInit, retriesRemaining?: number, delay?: number) => Promise<Response>;
package/package.json CHANGED
@@ -34,7 +34,7 @@
34
34
  "uuid": "^3.1.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@atlaskit/editor-common": "^110.38.0",
37
+ "@atlaskit/editor-common": "^110.39.0",
38
38
  "react": "^18.2.0"
39
39
  },
40
40
  "devDependencies": {
@@ -77,7 +77,7 @@
77
77
  }
78
78
  },
79
79
  "name": "@atlaskit/editor-synced-block-provider",
80
- "version": "2.12.2",
80
+ "version": "2.12.3",
81
81
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
82
82
  "author": "Atlassian Pty Ltd",
83
83
  "license": "Apache-2.0",