@rabbitio/ui-kit 1.0.0-beta.41 → 1.0.0-beta.43

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 (112) hide show
  1. package/.gitlab-ci.yml +29 -0
  2. package/.husky/commit-msg +8 -0
  3. package/.husky/pre-push +1 -0
  4. package/README.md +13 -4
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/clover.xml +6516 -0
  8. package/coverage/coverage-final.json +43 -0
  9. package/coverage/favicon.png +0 -0
  10. package/coverage/index.html +416 -0
  11. package/coverage/prettify.css +1 -0
  12. package/coverage/prettify.js +2 -0
  13. package/coverage/rabbit-ui-kit/index.html +116 -0
  14. package/coverage/rabbit-ui-kit/index.js.html +88 -0
  15. package/coverage/rabbit-ui-kit/src/common/adapters/axiosAdapter.js.html +190 -0
  16. package/coverage/rabbit-ui-kit/src/common/adapters/index.html +116 -0
  17. package/coverage/rabbit-ui-kit/src/common/amountUtils.js.html +1393 -0
  18. package/coverage/rabbit-ui-kit/src/common/errorUtils.js.html +211 -0
  19. package/coverage/rabbit-ui-kit/src/common/external-apis/apiGroups.js.html +250 -0
  20. package/coverage/rabbit-ui-kit/src/common/external-apis/index.html +131 -0
  21. package/coverage/rabbit-ui-kit/src/common/external-apis/ipAddressProviders.js.html +499 -0
  22. package/coverage/rabbit-ui-kit/src/common/fiatCurrenciesService.js.html +568 -0
  23. package/coverage/rabbit-ui-kit/src/common/index.html +146 -0
  24. package/coverage/rabbit-ui-kit/src/common/models/blockchain.js.html +115 -0
  25. package/coverage/rabbit-ui-kit/src/common/models/coin.js.html +556 -0
  26. package/coverage/rabbit-ui-kit/src/common/models/index.html +146 -0
  27. package/coverage/rabbit-ui-kit/src/common/models/protocol.js.html +100 -0
  28. package/coverage/rabbit-ui-kit/src/common/utils/cache.js.html +889 -0
  29. package/coverage/rabbit-ui-kit/src/common/utils/emailAPI.js.html +139 -0
  30. package/coverage/rabbit-ui-kit/src/common/utils/index.html +161 -0
  31. package/coverage/rabbit-ui-kit/src/common/utils/logging/index.html +131 -0
  32. package/coverage/rabbit-ui-kit/src/common/utils/logging/logger.js.html +229 -0
  33. package/coverage/rabbit-ui-kit/src/common/utils/logging/logsStorage.js.html +268 -0
  34. package/coverage/rabbit-ui-kit/src/common/utils/postponeExecution.js.html +118 -0
  35. package/coverage/rabbit-ui-kit/src/common/utils/safeStringify.js.html +235 -0
  36. package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/AssetIcon.jsx.html +250 -0
  37. package/coverage/rabbit-ui-kit/src/components/atoms/AssetIcon/index.html +116 -0
  38. package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/LoadingDots.jsx.html +256 -0
  39. package/coverage/rabbit-ui-kit/src/components/atoms/LoadingDots/index.html +116 -0
  40. package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/SupportChat.jsx.html +229 -0
  41. package/coverage/rabbit-ui-kit/src/components/atoms/SupportChat/index.html +116 -0
  42. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/Button.jsx.html +802 -0
  43. package/coverage/rabbit-ui-kit/src/components/atoms/buttons/Button/index.html +116 -0
  44. package/coverage/rabbit-ui-kit/src/components/hooks/index.html +131 -0
  45. package/coverage/rabbit-ui-kit/src/components/hooks/useCallHandlingErrors.js.html +163 -0
  46. package/coverage/rabbit-ui-kit/src/components/hooks/useReferredState.js.html +157 -0
  47. package/coverage/rabbit-ui-kit/src/components/utils/index.html +146 -0
  48. package/coverage/rabbit-ui-kit/src/components/utils/inputValueProviders.js.html +259 -0
  49. package/coverage/rabbit-ui-kit/src/components/utils/uiUtils.js.html +127 -0
  50. package/coverage/rabbit-ui-kit/src/components/utils/urlQueryUtils.js.html +346 -0
  51. package/coverage/rabbit-ui-kit/src/index.html +116 -0
  52. package/coverage/rabbit-ui-kit/src/index.js.html +250 -0
  53. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cacheAndConcurrentRequestsResolver.js.html +1762 -0
  54. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cachedRobustExternalApiCallerService.js.html +649 -0
  55. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/cancelProcessing.js.html +172 -0
  56. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/concurrentCalculationsMetadataHolder.js.html +394 -0
  57. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalApiProvider.js.html +553 -0
  58. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/externalServicesStatsCollector.js.html +331 -0
  59. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/index.html +206 -0
  60. package/coverage/rabbit-ui-kit/src/robustExteranlApiCallerService/robustExternalAPICallerService.js.html +1249 -0
  61. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +131 -0
  62. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +727 -0
  63. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +2899 -0
  64. package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +214 -0
  65. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +304 -0
  66. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +487 -0
  67. package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +146 -0
  68. package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
  69. package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +2191 -0
  70. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
  71. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +742 -0
  72. package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
  73. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +946 -0
  74. package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +116 -0
  75. package/coverage/rabbit-ui-kit/stories/atoms/index.html +116 -0
  76. package/coverage/sort-arrow-sprite.png +0 -0
  77. package/coverage/sorter.js +196 -0
  78. package/dist/index.cjs +1706 -1498
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.modern.js +817 -666
  81. package/dist/index.modern.js.map +1 -1
  82. package/dist/index.module.js +1704 -1498
  83. package/dist/index.module.js.map +1 -1
  84. package/dist/index.umd.js +1664 -1456
  85. package/dist/index.umd.js.map +1 -1
  86. package/package.json +11 -3
  87. package/src/common/amountUtils.js +4 -2
  88. package/src/common/external-apis/ipAddressProviders.js +138 -0
  89. package/src/common/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +14 -0
  90. package/src/common/utils/cache.js +4 -4
  91. package/src/components/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +139 -0
  92. package/src/components/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +71 -0
  93. package/src/components/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +144 -0
  94. package/src/components/utils/inputValueProviders.js +58 -0
  95. package/src/index.js +2 -0
  96. package/src/robustExteranlApiCallerService/robustExternalAPICallerService.js +4 -2
  97. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +787 -0
  98. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +745 -0
  99. package/src/robustExteranlApiCallerService/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +31 -0
  100. package/src/swaps-lib/external-apis/swapProvider.js +17 -4
  101. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +91 -30
  102. package/src/swaps-lib/models/baseSwapCreationInfo.js +4 -1
  103. package/src/swaps-lib/models/existingSwap.js +3 -0
  104. package/src/swaps-lib/models/existingSwapWithFiatData.js +4 -0
  105. package/src/swaps-lib/services/publicSwapService.js +32 -4
  106. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +506 -0
  107. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1311 -0
  108. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getAllSupportedCurrencies.test.js +76 -0
  109. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getDepositCurrencies.test.js +82 -0
  110. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1892 -0
  111. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getWithdrawalCurrencies.test.js +111 -0
  112. package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +88 -0
package/dist/index.umd.js CHANGED
@@ -1,14 +1,14 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('bignumber.js'), require('axios'), require('uuid'), require('jshashes'), require('eventbusjs')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'react', 'bignumber.js', 'axios', 'uuid', 'jshashes', 'eventbusjs'], factory) :
4
- (global = global || self, factory(global.uiKit = {}, global.react, global.bignumber_js, global.axios, global.uuid, global.jshashes, global.eventbusjs));
5
- })(this, (function (exports, React, bignumber_js, axios, uuid, Hashes, EventBusInstance) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('bignumber.js'), require('axios'), require('eventbusjs'), require('jshashes'), require('uuid')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'react', 'bignumber.js', 'axios', 'eventbusjs', 'jshashes', 'uuid'], factory) :
4
+ (global = global || self, factory(global.uiKit = {}, global.react, global.bignumber_js, global.axios, global.eventbusjs, global.jshashes, global.uuid));
5
+ })(this, (function (exports, React, bignumber_js, axios, EventBusInstance, Hashes, uuid) {
6
6
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
7
7
 
8
8
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
9
9
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
10
- var Hashes__default = /*#__PURE__*/_interopDefaultLegacy(Hashes);
11
10
  var EventBusInstance__default = /*#__PURE__*/_interopDefaultLegacy(EventBusInstance);
11
+ var Hashes__default = /*#__PURE__*/_interopDefaultLegacy(Hashes);
12
12
 
13
13
  function createCommonjsModule(fn) {
14
14
  var module = { exports: {} };
@@ -1628,7 +1628,7 @@
1628
1628
  return Logger;
1629
1629
  }();
1630
1630
 
1631
- function _catch$8(body, recover) {
1631
+ function _catch$9(body, recover) {
1632
1632
  try {
1633
1633
  var result = body();
1634
1634
  } catch (e) {
@@ -1644,7 +1644,7 @@
1644
1644
  setState = _useState[1];
1645
1645
  return React.useCallback(function (functionToBeCalled, event) {
1646
1646
  try {
1647
- var _temp = _catch$8(function () {
1647
+ var _temp = _catch$9(function () {
1648
1648
  return Promise.resolve(functionToBeCalled(event)).then(function () {});
1649
1649
  }, function (error) {
1650
1650
  Logger.logError(error, (functionToBeCalled == null ? void 0 : functionToBeCalled.name) || "errorBoundaryTrigger", "Caught by ErrorBoundary");
@@ -1697,6 +1697,60 @@
1697
1697
  };
1698
1698
  };
1699
1699
 
1700
+ var InputValuesProviders = /*#__PURE__*/function () {
1701
+ function InputValuesProviders() {}
1702
+ /**
1703
+ * Designed to be called onKeyUp event of html input field for float value
1704
+ * Removes all prohibited stuff from the given float string and remains only allowed.
1705
+ * Removes digits before and after the dot.
1706
+ *
1707
+ * @param inputString {string} string to be corrected
1708
+ * @param maxValue {string} max value for the correcting float value
1709
+ * @param digitsAfterDot {number} count of digits after the dot that this method should provide, min 0
1710
+ * @return {string} corrected float value string
1711
+ */
1712
+ InputValuesProviders.provideFormatOfFloatValueByInputString = function provideFormatOfFloatValueByInputString(inputString, digitsAfterDot, maxValue) {
1713
+ var _parts$2;
1714
+ if (digitsAfterDot === void 0) {
1715
+ digitsAfterDot = 2;
1716
+ }
1717
+ if (maxValue === void 0) {
1718
+ maxValue = null;
1719
+ }
1720
+ var value = inputString;
1721
+ if (!value) {
1722
+ return "";
1723
+ }
1724
+ if (digitsAfterDot < 0) {
1725
+ throw new Error("Min suffix length is 0, got " + digitsAfterDot);
1726
+ }
1727
+ value = value.replace(/[,]/g, "."); // replaces commas with dots
1728
+ value = value.replace(/[^0-9.]/g, ""); // remove non digits/dots
1729
+ value = value.replace(/^\./g, "0."); // adds leading zero
1730
+ value = value.replace(/\.+/g, "."); // replaces series of dots with single dot
1731
+
1732
+ var parts = value.split(".");
1733
+ if (parts.length > 2) {
1734
+ // removes all after second dot and itself
1735
+ parts = [parts[0], parts[1]];
1736
+ }
1737
+ if (maxValue != null) {
1738
+ var _parts$;
1739
+ var maxDigitsCountBeforeTheDot = bignumber_js.BigNumber(maxValue).toFixed(0).length;
1740
+ if (((_parts$ = parts[0]) == null ? void 0 : _parts$.length) > maxDigitsCountBeforeTheDot) {
1741
+ // removes redundant prefix digits
1742
+ parts[0] = parts[0].substring(parts[0].length - maxDigitsCountBeforeTheDot, parts[0].length);
1743
+ }
1744
+ }
1745
+ if (((_parts$2 = parts[1]) == null ? void 0 : _parts$2.length) > digitsAfterDot) {
1746
+ // removes redundant suffix digits
1747
+ parts[1] = parts[1].substring(0, digitsAfterDot);
1748
+ }
1749
+ return parts.join(".");
1750
+ };
1751
+ return InputValuesProviders;
1752
+ }();
1753
+
1700
1754
  var PARAMETER_VALUES_SEPARATOR = "|*|"; // Sting that with high probability will not be in the user's data
1701
1755
 
1702
1756
  /**
@@ -2097,11 +2151,13 @@
2097
2151
  }
2098
2152
 
2099
2153
  /**
2154
+ * Returns integer part of number as a string.
2155
+ *
2100
2156
  * @param amount {BigNumber|number|string|null|undefined} The number value to be trimmed.
2101
2157
  * HEX strings also allowed "0x..." and JS hex numbers
2102
2158
  * @return {string|null}
2103
2159
  */;
2104
- AmountUtils.intStr = function intStr(amount) {
2160
+ AmountUtils.toIntegerString = function toIntegerString(amount) {
2105
2161
  return this.trim(amount, 0);
2106
2162
  }
2107
2163
 
@@ -2188,7 +2244,7 @@
2188
2244
  leftNumber = leftNumber.times(multiplier);
2189
2245
  }
2190
2246
  }
2191
- var leftAmountString = AmountUtils.intStr(leftNumber);
2247
+ var leftAmountString = AmountUtils.toIntegerString(leftNumber);
2192
2248
  var rightAmountString = right != null ? right.toFixed(rightCurrencyDigitsAfterDots, bignumber_js.BigNumber.ROUND_FLOOR) : null;
2193
2249
  return leftAmountString + " " + leftTicker + " ~ " + (rightAmountString != null ? rightAmountString : "?") + " " + rightTicker;
2194
2250
  } catch (e) {
@@ -2424,10 +2480,13 @@
2424
2480
  */
2425
2481
  var Cache = /*#__PURE__*/function () {
2426
2482
  /**
2427
- * @param eventBus {EventBus} EventBus.js lib instance
2428
- * @param [noSessionEvents=[]] {string[]} array of events that will be treated as "no session"
2483
+ * @param [eventBus=null] {EventBus} EventBus.js lib instance if you plan to use Cache with events handling
2484
+ * @param [noSessionEvents=[]] {string[]} array of events that will be treated as "no session", you should pass EventBus to make it work
2429
2485
  */
2430
2486
  function Cache(eventBus, noSessionEvents) {
2487
+ if (eventBus === void 0) {
2488
+ eventBus = null;
2489
+ }
2431
2490
  if (noSessionEvents === void 0) {
2432
2491
  noSessionEvents = [];
2433
2492
  }
@@ -2523,7 +2582,7 @@
2523
2582
  });
2524
2583
  if (eventAndKeys) {
2525
2584
  eventAndKeys.push(key);
2526
- } else {
2585
+ } else if (_this._eventBus) {
2527
2586
  _this._eventDependentDataKeys.push([event, key]);
2528
2587
  _this._eventBus.addEventListener(event, function () {
2529
2588
  try {
@@ -2682,7 +2741,7 @@
2682
2741
  return Cache;
2683
2742
  }();
2684
2743
 
2685
- function _catch$7(body, recover) {
2744
+ function _catch$8(body, recover) {
2686
2745
  try {
2687
2746
  var result = body();
2688
2747
  } catch (e) {
@@ -2700,7 +2759,7 @@
2700
2759
  return new Promise(function (resolve, reject) {
2701
2760
  setTimeout(function () {
2702
2761
  try {
2703
- var _temp = _catch$7(function () {
2762
+ var _temp = _catch$8(function () {
2704
2763
  return Promise.resolve(execution()).then(function (_execution) {
2705
2764
  resolve(_execution);
2706
2765
  });
@@ -2784,7 +2843,7 @@
2784
2843
  return AxiosAdapter;
2785
2844
  }();
2786
2845
 
2787
- function _catch$6(body, recover) {
2846
+ function _catch$7(body, recover) {
2788
2847
  try {
2789
2848
  var result = body();
2790
2849
  } catch (e) {
@@ -2800,7 +2859,7 @@
2800
2859
  EmailsApi.sendEmail = function sendEmail(subject, body) {
2801
2860
  try {
2802
2861
  var _this = this;
2803
- var _temp = _catch$6(function () {
2862
+ var _temp = _catch$7(function () {
2804
2863
  var url = window.location.protocol + "//" + window.location.host + "/api/v1/" + _this.serverEndpointEntity;
2805
2864
  return Promise.resolve(axios__default["default"].post(url, {
2806
2865
  subject: subject,
@@ -2818,736 +2877,281 @@
2818
2877
  }();
2819
2878
  EmailsApi.serverEndpointEntity = "emails";
2820
2879
 
2821
- /**
2822
- * This util helps to avoid duplicated calls to a shared resource.
2823
- * It tracks is there currently active calculation for the specific cache id and make all other requests
2824
- * with the same cache id waiting for this active calculation to be finished. When the calculation ends
2825
- * the resolver allows all the waiting requesters to get the data from cache and start their own calculations.
2826
- *
2827
- * This class should be instantiated inside some other service where you need to request some resource concurrently.
2828
- * Rules:
2829
- * 1. When you need to make a request inside your main service call 'getCachedOrWaitForCachedOrAcquireLock'
2830
- * on the instance of this class and await for the result. If the flag allowing to start calculation is true
2831
- * then you can request data inside your main service. Otherwise you should use the cached data as an another
2832
- * requester just finished the most resent requesting and there is actual data in the cache that
2833
- * is returned to you here.
2834
- * 1.1 Also you can acquire a lock directly if you don't want to get cached data. Use the corresponding method 'acquireLock'.
2835
- *
2836
- * 2. If you start requesting (when you successfully acquired the lock) then after receiving the result of your
2837
- * requesting you should call the 'saveCachedData' so the retrieved data will appear in the cache.
2838
- *
2839
- * 3. If you successfully acquired the lock then you should after calling the 'saveCachedData' call
2840
- * the 'releaseLock' - this is mandatory to release the lock and allow other requesters to perform their requests.
2841
- * WARNING: If for any reason you forget to call this method then this class instance will wait perpetually for
2842
- * the lock releasing and all your attempts to request the data will constantly fail. So usually call it
2843
- * inside the 'finally' block.
2844
- *
2845
- * TODO: [tests, critical++] add unit tests - massively used logic and can produce sophisticated concurrency bugs
2846
- */
2847
-
2848
- function _settle$2(pact, state, value) {
2849
- if (!pact.s) {
2850
- if (value instanceof _Pact$2) {
2851
- if (value.s) {
2852
- if (state & 1) {
2853
- state = value.s;
2854
- }
2855
- value = value.v;
2856
- } else {
2857
- value.o = _settle$2.bind(null, pact, state);
2858
- return;
2859
- }
2860
- }
2861
- if (value && value.then) {
2862
- value.then(_settle$2.bind(null, pact, state), _settle$2.bind(null, pact, 2));
2863
- return;
2880
+ var ExternalApiProvider = /*#__PURE__*/function () {
2881
+ /**
2882
+ * Creates an instance of external api provider.
2883
+ *
2884
+ * If you need sub-request then use 'subRequestIndex' to check current request index in functions below.
2885
+ * Also use array for 'httpMethod'.
2886
+ *
2887
+ * If the endpoint of dedicated provider has pagination then you should customize the behavior using
2888
+ * "changeQueryParametersForPageNumber", "checkWhetherResponseIsForLastPage".
2889
+ *
2890
+ * We perform RPS counting all over the App to avoid blocking our clients due to abuses of the providers.
2891
+ *
2892
+ * @param endpoint {string} URL to the provider's endpoint. Note: you can customize it using composeQueryString
2893
+ * @param [httpMethod] {string|string[]} one of "get", "post", "put", "patch", "delete" or an array of these values
2894
+ * for request having sub-requests
2895
+ * @param [timeout] {number} number of milliseconds to wait for the response
2896
+ * @param [apiGroup] {ApiGroup} singleton object containing parameters of API group. Helpful when you use the same
2897
+ * api for different providers to avoid hardcoding RPS inside each provider what can cause mistakes
2898
+ * @param [specificHeaders] {Object} contains specific keys (headers) and values (their content) if needed for this provider
2899
+ * @param [maxPageLength] {number} optional number of items per page if the request supports pagination
2900
+ */
2901
+ function ExternalApiProvider(endpoint, httpMethod, timeout, apiGroup, specificHeaders, maxPageLength) {
2902
+ var _maxPageLength, _specificHeaders;
2903
+ if (specificHeaders === void 0) {
2904
+ specificHeaders = {};
2864
2905
  }
2865
- pact.s = state;
2866
- pact.v = value;
2867
- const observer = pact.o;
2868
- if (observer) {
2869
- observer(pact);
2906
+ if (maxPageLength === void 0) {
2907
+ maxPageLength = Number.MAX_SAFE_INTEGER;
2870
2908
  }
2909
+ this.endpoint = endpoint;
2910
+ this.httpMethod = httpMethod != null ? httpMethod : "get";
2911
+ // TODO: [refactoring, critical] We have two timeouts for robust data retrieval - here and inside the service method call, need to remain the only
2912
+ this.timeout = timeout != null ? timeout : 10000;
2913
+ // TODO: [refactoring, critical] We need single place for all RPSes as we use them as hardcoded constants now inside different services
2914
+ this.apiGroup = apiGroup;
2915
+ this.maxPageLength = (_maxPageLength = maxPageLength) != null ? _maxPageLength : Number.MAX_SAFE_INTEGER;
2916
+ this.niceFactor = 1;
2917
+ this.specificHeaders = (_specificHeaders = specificHeaders) != null ? _specificHeaders : {};
2871
2918
  }
2872
- }
2873
-
2874
- /**
2875
- * Util class to control access to a resource when it can be called in parallel for the same result.
2876
- * (E.g. getting today coins-fiat rates from some API).
2877
- */
2878
- var _Pact$2 = /*#__PURE__*/function () {
2879
- function _Pact() {}
2880
- _Pact.prototype.then = function (onFulfilled, onRejected) {
2881
- var result = new _Pact();
2882
- var state = this.s;
2883
- if (state) {
2884
- var callback = state & 1 ? onFulfilled : onRejected;
2885
- if (callback) {
2886
- try {
2887
- _settle$2(result, 1, callback(this.v));
2888
- } catch (e) {
2889
- _settle$2(result, 2, e);
2890
- }
2891
- return result;
2892
- } else {
2893
- return this;
2894
- }
2895
- }
2896
- this.o = function (_this) {
2897
- try {
2898
- var value = _this.v;
2899
- if (_this.s & 1) {
2900
- _settle$2(result, 1, onFulfilled ? onFulfilled(value) : value);
2901
- } else if (onRejected) {
2902
- _settle$2(result, 1, onRejected(value));
2903
- } else {
2904
- _settle$2(result, 2, value);
2905
- }
2906
- } catch (e) {
2907
- _settle$2(result, 2, e);
2908
- }
2909
- };
2910
- return result;
2919
+ var _proto = ExternalApiProvider.prototype;
2920
+ _proto.getRps = function getRps() {
2921
+ var _this$apiGroup$rps;
2922
+ return (_this$apiGroup$rps = this.apiGroup.rps) != null ? _this$apiGroup$rps : 2;
2911
2923
  };
2912
- return _Pact;
2913
- }();
2914
- function _isSettledPact$2(thenable) {
2915
- return thenable instanceof _Pact$2 && thenable.s & 1;
2916
- }
2917
- function _for$1(test, update, body) {
2918
- var stage;
2919
- for (;;) {
2920
- var shouldContinue = test();
2921
- if (_isSettledPact$2(shouldContinue)) {
2922
- shouldContinue = shouldContinue.v;
2923
- }
2924
- if (!shouldContinue) {
2925
- return result;
2926
- }
2927
- if (shouldContinue.then) {
2928
- stage = 0;
2929
- break;
2930
- }
2931
- var result = body();
2932
- if (result && result.then) {
2933
- if (_isSettledPact$2(result)) {
2934
- result = result.s;
2935
- } else {
2936
- stage = 1;
2937
- break;
2938
- }
2939
- }
2940
- if (update) {
2941
- var updateValue = update();
2942
- if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
2943
- stage = 2;
2944
- break;
2945
- }
2946
- }
2924
+ _proto.isRpsExceeded = function isRpsExceeded() {
2925
+ return this.apiGroup.isRpsExceeded();
2926
+ };
2927
+ _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
2928
+ this.apiGroup.actualizeLastCalledTimestamp();
2929
+ };
2930
+ _proto.getApiGroupId = function getApiGroupId() {
2931
+ return this.apiGroup.id;
2947
2932
  }
2948
- var pact = new _Pact$2();
2949
- var reject = _settle$2.bind(null, pact, 2);
2950
- (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
2951
- return pact;
2952
- function _resumeAfterBody(value) {
2953
- result = value;
2954
- do {
2955
- if (update) {
2956
- updateValue = update();
2957
- if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
2958
- updateValue.then(_resumeAfterUpdate).then(void 0, reject);
2959
- return;
2960
- }
2961
- }
2962
- shouldContinue = test();
2963
- if (!shouldContinue || _isSettledPact$2(shouldContinue) && !shouldContinue.v) {
2964
- _settle$2(pact, 1, result);
2965
- return;
2966
- }
2967
- if (shouldContinue.then) {
2968
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
2969
- return;
2970
- }
2971
- result = body();
2972
- if (_isSettledPact$2(result)) {
2973
- result = result.v;
2974
- }
2975
- } while (!result || !result.then);
2976
- result.then(_resumeAfterBody).then(void 0, reject);
2933
+
2934
+ /**
2935
+ * Some endpoint can require several sub requests. Example is one request to get confirmed transactions
2936
+ * and another request for unconfirmed transactions. You should override this method to return true for such requests.
2937
+ *
2938
+ * @return {boolean} true if this provider requires several requests to retrieve the data
2939
+ */;
2940
+ _proto.doesRequireSubRequests = function doesRequireSubRequests() {
2941
+ return false;
2977
2942
  }
2978
- function _resumeAfterTest(shouldContinue) {
2979
- if (shouldContinue) {
2980
- result = body();
2981
- if (result && result.then) {
2982
- result.then(_resumeAfterBody).then(void 0, reject);
2983
- } else {
2984
- _resumeAfterBody(result);
2985
- }
2986
- } else {
2987
- _settle$2(pact, 1, result);
2988
- }
2989
- }
2990
- function _resumeAfterUpdate() {
2991
- if (shouldContinue = test()) {
2992
- if (shouldContinue.then) {
2993
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
2994
- } else {
2995
- _resumeAfterTest(shouldContinue);
2996
- }
2997
- } else {
2998
- _settle$2(pact, 1, result);
2999
- }
3000
- }
3001
- }
3002
- function _catch$5(body, recover) {
3003
- try {
3004
- var result = body();
3005
- } catch (e) {
3006
- return recover(e);
3007
- }
3008
- if (result && result.then) {
3009
- return result.then(void 0, recover);
3010
- }
3011
- return result;
3012
- }
3013
- var CacheAndConcurrentRequestsResolver = /*#__PURE__*/function () {
3014
- /**
3015
- * @param bio {string} unique identifier for the exact service
3016
- * @param cache {Cache} cache
3017
- * @param cacheTtl {number|null} time to live for cache ms. 0 or null means the cache cannot expire
3018
- * @param [maxCallAttemptsToWaitForAlreadyRunningRequest=100] {number} number of request allowed to do waiting for
3019
- * result before we fail the original request. Use custom value only if you need to make the attempts count
3020
- * and polling interval changes.
3021
- * @param [timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished=1000] {number}
3022
- * timeout ms for polling for a result. if you change maxCallAttemptsToWaitForAlreadyRunningRequest
3023
- * then this parameter maybe also require the custom value.
3024
- * @param [removeExpiredCacheAutomatically=true] {boolean}
3025
- */
3026
- function CacheAndConcurrentRequestsResolver(bio, cache, cacheTtl, removeExpiredCacheAutomatically, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished) {
3027
- if (removeExpiredCacheAutomatically === void 0) {
3028
- removeExpiredCacheAutomatically = true;
3029
- }
3030
- if (maxCallAttemptsToWaitForAlreadyRunningRequest === void 0) {
3031
- maxCallAttemptsToWaitForAlreadyRunningRequest = 100;
3032
- }
3033
- if (timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished === void 0) {
3034
- timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished = 1000;
3035
- }
3036
- if (cacheTtl != null && cacheTtl < timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished * 2) {
3037
- /*
3038
- * During the lifetime of this service e.g. if the data is being retrieved slowly we can get
3039
- * RACE CONDITION when we constantly retrieve data and during retrieval it is expired, so we are trying
3040
- * to retrieve it again and again.
3041
- * We have a protection mechanism that we will wait no more than
3042
- * maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
3043
- * but this additional check is aimed to reduce potential loading time for some requests.
3044
- */
3045
- throw new Error("DEV: Wrong parameters passed to construct " + bio + " - TTL " + cacheTtl + " should be 2 times greater than " + timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
3046
- }
3047
- this._bio = bio;
3048
- this._cache = cache;
3049
- this._cacheTtlMs = cacheTtl != null ? cacheTtl : null;
3050
- this._maxExecutionTimeMs = maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished;
3051
- this._removeExpiredCacheAutomatically = removeExpiredCacheAutomatically;
3052
- this._requestsManager = new ManagerOfRequestsToTheSameResource(bio, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
2943
+
2944
+ /**
2945
+ * Some endpoint support pagination. Override this method if so and implement corresponding methods.
2946
+ *
2947
+ * @return {boolean} true if this provider requires several requests to retrieve the data
2948
+ */;
2949
+ _proto.doesSupportPagination = function doesSupportPagination() {
2950
+ return false;
3053
2951
  }
3054
2952
 
3055
2953
  /**
3056
- * When using this service this is the major method you should call to get data by cache id.
3057
- * This method checks is there cached data and ether
3058
- * - returns you flag that you can start requesting data from the shared resource
3059
- * - or if there is already started calculation waits until it is finished (removed from this service)
3060
- * and returns you the retrieved data
3061
- * - or just returns you the cached data
3062
- *
3063
- * 'canStartDataRetrieval' equal true means that the lock was acquired, and you should manually call 'saveCachedData'
3064
- * if needed and then 'releaseLock' to mark this calculation as finished so other
3065
- * requesters can take their share of the resource.
2954
+ * Composes a query string to be added to the endpoint of this provider.
3066
2955
  *
3067
- * @param cacheId {string}
3068
- * @return {Promise<({
3069
- * canStartDataRetrieval: true,
3070
- * cachedData: any,
3071
- * lockId: string
3072
- * }|{
3073
- * canStartDataRetrieval: false,
3074
- * cachedData: any
3075
- * })>}
3076
- */
3077
- var _proto = CacheAndConcurrentRequestsResolver.prototype;
3078
- _proto.getCachedOrWaitForCachedOrAcquireLock = function getCachedOrWaitForCachedOrAcquireLock(cacheId) {
3079
- try {
3080
- var _this = this;
3081
- return Promise.resolve(_catch$5(function () {
3082
- function _temp2() {
3083
- var _cached, _cached2;
3084
- return calculationId ? {
3085
- canStartDataRetrieval: true,
3086
- cachedData: (_cached = cached) != null ? _cached : cachedDataBackupIsPresentButExpired,
3087
- lockId: calculationId
3088
- } : {
3089
- canStartDataRetrieval: false,
3090
- cachedData: (_cached2 = cached) != null ? _cached2 : cachedDataBackupIsPresentButExpired
3091
- };
3092
- }
3093
- var startedAtTimestamp = Date.now();
3094
- var cached = _this._cache.get(cacheId);
3095
- var cachedDataBackupIsPresentButExpired = null;
3096
- if (cached != null && !_this._removeExpiredCacheAutomatically) {
3097
- var lastUpdateTimestamp = _this._cache.getLastUpdateTimestamp(cacheId);
3098
- if ((lastUpdateTimestamp != null ? lastUpdateTimestamp : 0) + _this._cacheTtlMs < Date.now()) {
3099
- /*
3100
- * Here we are manually clearing 'cached' value retrieved from cache to force the data loading.
3101
- * But we save its value first to the backup variable to be able to return this value if ongoing
3102
- * requesting fails.
3103
- */
3104
- cachedDataBackupIsPresentButExpired = cached;
3105
- cached = null;
3106
- }
3107
- }
3108
- var calculationId = null;
3109
- var isRetrievedCacheExpired = true;
3110
- var isWaitingForActiveCalculationSucceeded;
3111
- var weStillHaveSomeTimeToProceedExecution = true;
3112
- var _temp = _for$1(function () {
3113
- return calculationId == null && cached == null && !!isRetrievedCacheExpired && !!weStillHaveSomeTimeToProceedExecution;
3114
- }, void 0, function () {
3115
- return Promise.resolve(_this._requestsManager.startCalculationOrWaitForActiveToFinish(cacheId)).then(function (result) {
3116
- calculationId = typeof result === "string" ? result : null;
3117
- isWaitingForActiveCalculationSucceeded = typeof result === "boolean" ? result : null;
3118
- cached = _this._cache.get(cacheId);
3119
- isRetrievedCacheExpired = isWaitingForActiveCalculationSucceeded && cached == null;
3120
- weStillHaveSomeTimeToProceedExecution = Date.now() - startedAtTimestamp < _this._maxExecutionTimeMs;
3121
- });
3122
- });
3123
- return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
3124
- }, function (e) {
3125
- improveAndRethrow(e, _this._bio + ".getCachedOrWaitForCachedOrAcquireLock");
3126
- }));
3127
- } catch (e) {
3128
- return Promise.reject(e);
3129
- }
2956
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2957
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2958
+ * @returns {string} query string to be concatenated with endpoint
2959
+ */;
2960
+ _proto.composeQueryString = function composeQueryString(params, subRequestIndex) {
2961
+ return "";
3130
2962
  }
2963
+
3131
2964
  /**
3132
- * Returns just the current cache value for the given id.
3133
- * Doesn't wait for the active calculation, doesn't acquire lock, just retrieves the current cache as it is.
2965
+ * Composes a body to be added to the request
3134
2966
  *
3135
- * @param cacheId {string}
3136
- * @return {any}
3137
- */
3138
- ;
3139
- _proto.getCached = function getCached(cacheId) {
3140
- try {
3141
- return this._cache.get(cacheId);
3142
- } catch (e) {
3143
- improveAndRethrow(e, "getCached");
3144
- }
3145
- };
3146
- _proto._getTtl = function _getTtl() {
3147
- return this._removeExpiredCacheAutomatically ? this._cacheTtlMs : null;
2967
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2968
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2969
+ * @returns {string}
2970
+ */;
2971
+ _proto.composeBody = function composeBody(params, subRequestIndex) {
2972
+ return "";
3148
2973
  }
3149
2974
 
3150
2975
  /**
3151
- * Directly acquires the lock despite on cached data availability.
3152
- * So if this method returns result === true you can start the data retrieval.
2976
+ * Extracts data from the response and returns it
3153
2977
  *
3154
- * @param cacheId {string}
3155
- * @return {Promise<{ result: true, lockId: string }|{ result: false }>}
2978
+ * @param response {Object} HTTP response returned by provider
2979
+ * @param [params] {any[]} params array passed to the RobustExternalAPICallerService
2980
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2981
+ * @param iterationsData {any[]} array of data retrieved from previous sub-requests
2982
+ * @returns {any}
3156
2983
  */;
3157
- _proto.acquireLock = function acquireLock(cacheId) {
3158
- try {
3159
- var _this2 = this;
3160
- return Promise.resolve(_catch$5(function () {
3161
- return Promise.resolve(_this2._requestsManager.acquireLock(cacheId));
3162
- }, function (e) {
3163
- improveAndRethrow(e, "acquireLock");
3164
- }));
3165
- } catch (e) {
3166
- return Promise.reject(e);
3167
- }
2984
+ _proto.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
2985
+ return [];
3168
2986
  }
2987
+
3169
2988
  /**
3170
- * This method should be called only if you acquired a lock successfully.
3171
- *
3172
- * If the current lock id is not equal to the passed one the passed data will be ignored.
3173
- * Or you can do the synchronous data merging on your side and pass the
3174
- * wasDataMergedSynchronouslyWithMostRecentCacheState=true so your data will be stored
3175
- * despite on the lockId.
3176
- * WARNING: you should do this only if you are sure you perform the synchronous update.
2989
+ * Function changing the query string according to page number and previous response
2990
+ * Only for endpoints supporting pagination
3177
2991
  *
3178
- * @param cacheId {string}
3179
- * @param lockId {string}
3180
- * @param data {any}
3181
- * @param [sessionDependentData=true] {boolean}
3182
- * @param [wasDataMergedSynchronouslyWithMostRecentCacheState=false]
3183
- */
3184
- ;
3185
- _proto.saveCachedData = function saveCachedData(cacheId, lockId, data, sessionDependentData, wasDataMergedSynchronouslyWithMostRecentCacheState) {
3186
- if (sessionDependentData === void 0) {
3187
- sessionDependentData = true;
3188
- }
3189
- if (wasDataMergedSynchronouslyWithMostRecentCacheState === void 0) {
3190
- wasDataMergedSynchronouslyWithMostRecentCacheState = false;
3191
- }
3192
- try {
3193
- if (wasDataMergedSynchronouslyWithMostRecentCacheState || this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
3194
- /* We save passed data only if the <caller> has the currently acquired lockId.
3195
- * If the passed lockId is not the active one it means that other code cleared/stopped the lock
3196
- * acquired by the <caller> recently due to some urgent/more prior changes.
3197
- *
3198
- * But we allow user to pass the 'wasDataMergedSynchronouslyWithMostRecentCacheState' flag
3199
- * that tells us that the user had taken the most recent cache value and merged his new data
3200
- * with that cached value (AFTER possibly performing async data retrieval). This means that we
3201
- * can ignore the fact that his lockId is no more relevant and save the passed data
3202
- * as it is synchronously merged with the most recent cached data. (Synchronously merged means that
3203
- * the lost update cannot occur during the merge time as JS execute the synchronous functions\
3204
- * till the end).
3205
- */
3206
- if (sessionDependentData) {
3207
- this._cache.putSessionDependentData(cacheId, data, this._getTtl());
3208
- } else {
3209
- this._cache.put(cacheId, data, this._getTtl());
3210
- }
3211
- }
3212
- } catch (e) {
3213
- improveAndRethrow(e, this._bio + ".saveCachedData");
3214
- }
2992
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2993
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
2994
+ * @param pageNumber {number} new page number. We count from 0. You need to manually increment with 1 if your
2995
+ * provider counts pages starting with 1
2996
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2997
+ * @returns {any[]}
2998
+ */;
2999
+ _proto.changeQueryParametersForPageNumber = function changeQueryParametersForPageNumber(params, previousResponse, pageNumber, subRequestIndex) {
3000
+ return params;
3215
3001
  }
3216
3002
 
3217
3003
  /**
3218
- * Should be called then and only then if you successfully acquired a lock with the lock id.
3004
+ * Function checking whether the response is for the last page to stop requesting for a next page.
3005
+ * Only for endpoints supporting pagination.
3219
3006
  *
3220
- * @param cacheId {string}
3221
- * @param lockId {string}
3007
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
3008
+ * @param currentResponse {Object} HTTP response returned by provider for current call (current page, next after the previous)
3009
+ * @param currentPageNumber {number} current page number (for current response)
3010
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
3011
+ * @returns {boolean}
3222
3012
  */;
3223
- _proto.releaseLock = function releaseLock(cacheId, lockId) {
3224
- try {
3225
- if (this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
3226
- this._requestsManager.finishActiveCalculation(cacheId);
3227
- }
3228
- } catch (e) {
3229
- improveAndRethrow(e, this._bio + ".releaseLock");
3230
- }
3013
+ _proto.checkWhetherResponseIsForLastPage = function checkWhetherResponseIsForLastPage(previousResponse, currentResponse, currentPageNumber, subRequestIndex) {
3014
+ return true;
3231
3015
  }
3232
3016
 
3233
3017
  /**
3234
- * Actualized currently present cached data by key. Applies the provided function to the cached data.
3018
+ * Resets the nice factor to default value
3019
+ */;
3020
+ _proto.resetNiceFactor = function resetNiceFactor() {
3021
+ this.niceFactor = 1;
3022
+ }
3023
+
3024
+ /**
3025
+ * Internal method used for requests requiring sub-requests.
3235
3026
  *
3236
- * @param cacheId {string} id of cache entry
3237
- * @param synchronousCurrentCacheProcessor (function|null} synchronous function accepting cache entry. Should return
3238
- * an object in following format:
3239
- * {
3240
- * isModified: boolean,
3241
- * data: any
3242
- * }
3243
- * the flag signals whether data was changed during the processing or not
3244
- * @param [sessionDependent=true] {boolean} whether to mark the cache entry as session-dependent
3027
+ * @param iterationsData {any[]} iterations data retrieved from getDataByResponse called per sub-request.
3028
+ * @return {any} by default flatten the passed iterations data array. Should be redefined if you need another logic.
3245
3029
  */;
3246
- _proto.actualizeCachedData = function actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent) {
3247
- if (sessionDependent === void 0) {
3248
- sessionDependent = true;
3249
- }
3250
- try {
3251
- var cached = this._cache.get(cacheId);
3252
- var result = synchronousCurrentCacheProcessor(cached);
3253
- if (result != null && result.isModified && (result == null ? void 0 : result.data) != null) {
3254
- if (sessionDependent) {
3255
- this._cache.putSessionDependentData(cacheId, result == null ? void 0 : result.data, this._getTtl());
3256
- } else {
3257
- this._cache.put(cacheId, result == null ? void 0 : result.data, this._getTtl());
3258
- }
3030
+ _proto.incorporateIterationsData = function incorporateIterationsData(iterationsData) {
3031
+ return iterationsData.flat();
3032
+ };
3033
+ return ExternalApiProvider;
3034
+ }();
3259
3035
 
3260
- /* Here we call the lock releasing to ensure the currently active calculation will be ignored.
3261
- * This is needed to ensure no 'lost update'.
3262
- * Lost update can occur if we change data in this method and after that some calculation finishes
3263
- * having the earlier data as its base to calculate its data set result. And the earlier data
3264
- * has no changes applied inside this method, so we will lose them.
3265
- *
3266
- * This is not so good solution: ideally, we should acquire lock before performing any data updating.
3267
- * But the goal of this method is to provide an instant ability to update the cached data.
3268
- * And if we start acquiring the lock here the data update can be postponed significantly.
3269
- * And this kills the desired nature of this method.
3270
- * So we better lose some data retrieval (means abusing the resource a bit) than lose
3271
- * the instant update expected after this method execution.
3272
- */
3273
- this._requestsManager.finishActiveCalculation(cacheId);
3274
- }
3275
- } catch (e) {
3276
- improveAndRethrow(e, this._bio + ".actualizeCachedData");
3036
+ /**
3037
+ * Models a group of APIs provided by the same owner and used for different services in our app.
3038
+ * It means we need to mention RPS several times for each usage and also have some holder of last call timestamp per
3039
+ * api group. So this concept allows to use it for exact ExternalApiProvider and make sure that you use the same
3040
+ * RPS value and make decisions on base of the same timestamp of last call to the API group owner.
3041
+ */
3042
+ var ApiGroup = /*#__PURE__*/function () {
3043
+ function ApiGroup(id, rps, backendProxyIdGenerator) {
3044
+ if (backendProxyIdGenerator === void 0) {
3045
+ backendProxyIdGenerator = null;
3277
3046
  }
3047
+ this.id = id;
3048
+ this.rps = rps;
3049
+ this.lastCalledTimestamp = null;
3050
+ this.backendProxyIdGenerator = backendProxyIdGenerator;
3051
+ }
3052
+ var _proto = ApiGroup.prototype;
3053
+ _proto.isRpsExceeded = function isRpsExceeded() {
3054
+ var _this$lastCalledTimes;
3055
+ return ((_this$lastCalledTimes = this.lastCalledTimestamp) != null ? _this$lastCalledTimes : 0) + Math.floor(1000 / this.rps) > Date.now();
3278
3056
  };
3279
- _proto.invalidate = function invalidate(key) {
3280
- this._cache.invalidate(key);
3281
- this._requestsManager.finishActiveCalculation(key);
3282
- };
3283
- _proto.invalidateContaining = function invalidateContaining(keyPart) {
3284
- this._cache.invalidateContaining(keyPart);
3285
- this._requestsManager.finishAllActiveCalculations(keyPart);
3286
- };
3287
- _proto.markAsExpiredButDontRemove = function markAsExpiredButDontRemove(key) {
3288
- if (this._removeExpiredCacheAutomatically) {
3289
- this._cache.markCacheItemAsExpiredButDontRemove(key, this._cacheTtlMs);
3290
- } else {
3291
- this._cache.setLastUpdateTimestamp(key, Date.now() - this._cacheTtlMs - 1);
3292
- }
3293
- this._requestsManager.finishAllActiveCalculations(key);
3057
+ _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
3058
+ this.lastCalledTimestamp = Date.now();
3294
3059
  };
3295
- return CacheAndConcurrentRequestsResolver;
3060
+ return ApiGroup;
3296
3061
  }();
3297
- var ManagerOfRequestsToTheSameResource = /*#__PURE__*/function () {
3062
+ var ApiGroups = {
3298
3063
  /**
3299
- * @param bio {string} resource-related identifier for logging
3300
- * @param [maxPollsCount=100] {number} max number of attempts to wait when waiting for a lock acquisition
3301
- * @param [timeoutDuration=1000] {number} timeout between the polls for a lock acquisition
3064
+ * Currently we use free version of etherscan provider with 0.2 RPS. But we have API key with 100k requests free
3065
+ * per month. So we can add it if not enough current RPS.
3302
3066
  */
3303
- function ManagerOfRequestsToTheSameResource(bio, maxPollsCount, timeoutDuration) {
3304
- if (maxPollsCount === void 0) {
3305
- maxPollsCount = 100;
3067
+ ETHERSCAN: new ApiGroup("etherscan", 0.17),
3068
+ // Actually 0.2 but fails sometime, so we use smaller
3069
+ ALCHEMY: new ApiGroup("alchemy", 0.3, function (networkKey) {
3070
+ return "alchemy-" + networkKey;
3071
+ }),
3072
+ BLOCKSTREAM: new ApiGroup("blockstream", 0.2),
3073
+ BLOCKCHAIN_INFO: new ApiGroup("blockchain.info", 1),
3074
+ BLOCKNATIVE: new ApiGroup("blocknative", 0.5),
3075
+ ETHGASSTATION: new ApiGroup("ethgasstation", 0.5),
3076
+ TRONGRID: new ApiGroup("trongrid", 0.3, function (networkKey) {
3077
+ return "trongrid-" + networkKey;
3078
+ }),
3079
+ TRONSCAN: new ApiGroup("tronscan", 0.3),
3080
+ GETBLOCK: new ApiGroup("getblock", 0.3),
3081
+ COINCAP: new ApiGroup("coincap", 0.5),
3082
+ // 200 per minute without API key
3083
+ COINGECKO: new ApiGroup("coingecko", 0.9),
3084
+ // actually 0.13-0.5 according to the docs but we use smaller due to expirienced frequent abuses
3085
+ MESSARI: new ApiGroup("messari", 0.2),
3086
+ BTCCOM: new ApiGroup("btccom", 0.2),
3087
+ BITAPS: new ApiGroup("bitaps", 0.25),
3088
+ // Docs say that RPS is 3 but using it causes frequent 429 HTTP errors
3089
+ CEX: new ApiGroup("cex", 0.5),
3090
+ // Just assumption for RPS
3091
+ BIGDATACLOUD: new ApiGroup("bigdatacloud", 1),
3092
+ // Just assumption for RPS
3093
+ TRACKIP: new ApiGroup("trackip", 1),
3094
+ // Just assumption for RPS
3095
+ IPIFY: new ApiGroup("ipify", 1),
3096
+ // Just assumption for RPS
3097
+ WHATISMYIPADDRESS: new ApiGroup("whatismyipaddress", 1),
3098
+ // Just assumption for RPS
3099
+ EXCHANGERATE: new ApiGroup("exchangerate", 1),
3100
+ // Just assumption for RPS
3101
+ FRANKFURTER: new ApiGroup("frankfurter", 1),
3102
+ // Just assumption for RPS
3103
+ BITGO: new ApiGroup("bitgo", 1),
3104
+ // Just assumption for RPS
3105
+ BITCOINER: new ApiGroup("bitcoiner", 1),
3106
+ // Just assumption for RPS
3107
+ BITCORE: new ApiGroup("bitcore", 1),
3108
+ // Just assumption for RPS
3109
+ // BLOCKCHAIR: new ApiGroup("blockchair", 0.04), // this provider require API key for commercial use (10usd 10000 reqs), we will add it later
3110
+ MEMPOOL: new ApiGroup("mempool", 0.2) // Just assumption for RPS
3111
+ };
3112
+
3113
+ // TODO: [refactoring, low] Consider removing this logic task_id=c360f2af75764bde8badd9ff1cc00d48
3114
+ var ConcurrentCalculationsMetadataHolder = /*#__PURE__*/function () {
3115
+ function ConcurrentCalculationsMetadataHolder() {
3116
+ this._calculations = {};
3117
+ }
3118
+ var _proto = ConcurrentCalculationsMetadataHolder.prototype;
3119
+ _proto.startCalculation = function startCalculation(domain, calculationsHistoryMaxLength) {
3120
+ if (calculationsHistoryMaxLength === void 0) {
3121
+ calculationsHistoryMaxLength = 100;
3306
3122
  }
3307
- if (timeoutDuration === void 0) {
3308
- timeoutDuration = 1000;
3123
+ if (!this._calculations[domain]) {
3124
+ this._calculations[domain] = [];
3125
+ }
3126
+ if (this._calculations[domain].length > calculationsHistoryMaxLength) {
3127
+ this._calculations[domain] = this._calculations[domain].slice(Math.round(calculationsHistoryMaxLength * 0.2));
3128
+ }
3129
+ var newCalculation = {
3130
+ startTimestamp: Date.now(),
3131
+ endTimestamp: null,
3132
+ uuid: uuid.v4()
3133
+ };
3134
+ this._calculations[domain].push(newCalculation);
3135
+ return newCalculation.uuid;
3136
+ };
3137
+ _proto.endCalculation = function endCalculation(domain, uuid, isFailed) {
3138
+ if (isFailed === void 0) {
3139
+ isFailed = false;
3309
3140
  }
3310
- this.bio = bio;
3311
- this.maxPollsCount = maxPollsCount;
3312
- this.timeoutDuration = timeoutDuration;
3313
- this._activeCalculationsIds = new Map();
3314
- this._nextCalculationIds = new Map();
3315
- }
3316
-
3317
- /**
3318
- * If there is no active calculation just creates uuid and returns it.
3319
- * If there is active calculation waits until it removed from the active calculation uuid variable.
3320
- *
3321
- * @param requestHash {string}
3322
- * @return {Promise<string|boolean>} returns uuid of new active calculation or true if waiting for active
3323
- * calculation succeed or false if max attempts count exceeded
3324
- */
3325
- var _proto2 = ManagerOfRequestsToTheSameResource.prototype;
3326
- _proto2.startCalculationOrWaitForActiveToFinish = function startCalculationOrWaitForActiveToFinish(requestHash) {
3327
3141
  try {
3328
- var _exit;
3329
- var _this3 = this;
3330
- var _temp3 = _catch$5(function () {
3331
- var activeCalculationIdForHash = _this3._activeCalculationsIds.get(requestHash);
3332
- if (activeCalculationIdForHash == null) {
3333
- var id = uuid.v4();
3334
- _this3._activeCalculationsIds.set(requestHash, id);
3335
- _exit = 1;
3336
- return id;
3337
- }
3338
- return Promise.resolve(_this3._waitForCalculationIdToFinish(requestHash, activeCalculationIdForHash, 0)).then(function (_await$_this3$_waitFo) {
3339
- _exit = 1;
3340
- return _await$_this3$_waitFo;
3341
- });
3342
- }, function (e) {
3343
- Logger.logError(e, "startCalculationOrWaitForActiveToFinish_" + _this3.bio);
3142
+ var _calculation$endTimes, _calculation$startTim, _calculation$uuid;
3143
+ var calculation = this._calculations[domain].find(function (calculation) {
3144
+ return (calculation == null ? void 0 : calculation.uuid) === uuid;
3344
3145
  });
3345
- return Promise.resolve(_temp3 && _temp3.then ? _temp3.then(function (_result3) {
3346
- return _exit ? _result3 : null;
3347
- }) : _exit ? _temp3 : null);
3146
+ if (calculation) {
3147
+ calculation.endTimestamp = Date.now();
3148
+ calculation.isFiled = isFailed;
3149
+ }
3150
+ var elapsed = ((((_calculation$endTimes = calculation == null ? void 0 : calculation.endTimestamp) != null ? _calculation$endTimes : 0) - ((_calculation$startTim = calculation == null ? void 0 : calculation.startTimestamp) != null ? _calculation$startTim : 0)) / 1000).toFixed(1);
3151
+ Logger.log("endCalculation", elapsed + " ms: " + domain + "." + ((_calculation$uuid = calculation == null ? void 0 : calculation.uuid) != null ? _calculation$uuid : "").slice(0, 7));
3152
+ return calculation;
3348
3153
  } catch (e) {
3349
- return Promise.reject(e);
3350
- }
3351
- }
3352
- /**
3353
- * Acquires lock to the resource by the provided hash.
3354
- *
3355
- * @param requestHash {string}
3356
- * @return {Promise<{ result: true, lockId: string }|{ result: false }>} result is true if the lock is successfully
3357
- * acquired, false if the max allowed time to wait for acquisition expired or any unexpected error occurs
3358
- * during the waiting.
3359
- */
3360
- ;
3361
- _proto2.acquireLock = function acquireLock(requestHash) {
3362
- try {
3363
- var _this4 = this;
3364
- return Promise.resolve(_catch$5(function () {
3365
- var _this4$_nextCalculati;
3366
- var activeId = _this4._activeCalculationsIds.get(requestHash);
3367
- var nextId = uuid.v4();
3368
- if (activeId == null) {
3369
- _this4._activeCalculationsIds.set(requestHash, nextId);
3370
- return {
3371
- result: true,
3372
- lockId: nextId
3373
- };
3374
- }
3375
- var currentNext = (_this4$_nextCalculati = _this4._nextCalculationIds.get(requestHash)) != null ? _this4$_nextCalculati : [];
3376
- currentNext.push(nextId);
3377
- _this4._nextCalculationIds.set(requestHash, currentNext);
3378
- return Promise.resolve(_this4._waitForCalculationIdToFinish(requestHash, activeId, 0, nextId)).then(function (waitingResult) {
3379
- return {
3380
- result: waitingResult,
3381
- lockId: waitingResult ? nextId : undefined
3382
- };
3383
- });
3384
- }, function (e) {
3385
- improveAndRethrow(e, "acquireLock");
3386
- }));
3387
- } catch (e) {
3388
- return Promise.reject(e);
3389
- }
3390
- }
3391
- /**
3392
- * Clears active calculation id.
3393
- * WARNING: if you forget to call this method the start* one will perform maxPollsCount attempts before finishing
3394
- * @param requestHash {string} hash of request. Helps to distinct the request for the same resource but
3395
- * having different request parameters and hold a dedicated calculation id per this hash
3396
- */
3397
- ;
3398
- _proto2.finishActiveCalculation = function finishActiveCalculation(requestHash) {
3399
- if (requestHash === void 0) {
3400
- requestHash = "default";
3401
- }
3402
- try {
3403
- var _this$_nextCalculatio;
3404
- this._activeCalculationsIds["delete"](requestHash);
3405
- var next = (_this$_nextCalculatio = this._nextCalculationIds.get(requestHash)) != null ? _this$_nextCalculatio : [];
3406
- if (next.length) {
3407
- this._activeCalculationsIds.set(requestHash, next[0]);
3408
- this._nextCalculationIds.set(requestHash, next.slice(1));
3409
- }
3410
- } catch (e) {
3411
- improveAndRethrow(e, "finishActiveCalculation");
3412
- }
3413
- };
3414
- _proto2.finishAllActiveCalculations = function finishAllActiveCalculations(keyPart) {
3415
- var _this5 = this;
3416
- if (keyPart === void 0) {
3417
- keyPart = "";
3418
- }
3419
- try {
3420
- Array.from(this._activeCalculationsIds.keys()).forEach(function (hash) {
3421
- if (typeof hash === "string" && new RegExp(keyPart).test(hash)) {
3422
- _this5.finishActiveCalculation(hash);
3423
- }
3424
- });
3425
- } catch (e) {
3426
- improveAndRethrow(e, "finishAllActiveCalculations");
3427
- }
3428
- }
3429
-
3430
- /**
3431
- * @param requestHash {string}
3432
- * @param lockId {string}
3433
- * @return {boolean}
3434
- */;
3435
- _proto2.isTheLockActiveOne = function isTheLockActiveOne(requestHash, lockId) {
3436
- try {
3437
- return this._activeCalculationsIds.get(requestHash) === lockId;
3438
- } catch (e) {
3439
- improveAndRethrow(e, "isTheLockActiveOne");
3440
- }
3441
- }
3442
-
3443
- /**
3444
- * @param requestHash {string}
3445
- * @param activeCalculationId {string|null}
3446
- * @param [attemptIndex=0] {number}
3447
- * @param waitForCalculationId {string|null} if you want to wait for an exact id to appear as active then pass this parameter
3448
- * @return {Promise<boolean>} true
3449
- * - if the given calculation id is no more an active one
3450
- * - or it is equal to waitForCalculationId
3451
- * false
3452
- * - if waiting period exceeds the max allowed waiting time or unexpected error occurs
3453
- * @private
3454
- */;
3455
- _proto2._waitForCalculationIdToFinish = function _waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex, waitForCalculationId) {
3456
- if (attemptIndex === void 0) {
3457
- attemptIndex = 0;
3458
- }
3459
- if (waitForCalculationId === void 0) {
3460
- waitForCalculationId = null;
3461
- }
3462
- try {
3463
- var _this6 = this;
3464
- try {
3465
- if (attemptIndex + 1 > _this6.maxPollsCount) {
3466
- // Max number of polls for active calculation id change is achieved. So we return false.
3467
- return Promise.resolve(false);
3468
- }
3469
- var currentId = _this6._activeCalculationsIds.get(requestHash);
3470
- if (waitForCalculationId == null ? currentId !== activeCalculationId : currentId === waitForCalculationId) {
3471
- /* We return true depending on the usage of this function:
3472
- * 1. if there is calculation id that we should wait for to become an active then we return true only
3473
- * if this id becomes the active one.
3474
- *
3475
- * Theoretically we can fail to wait for the desired calculation id. This can be caused by wrong use of
3476
- * this service or by any other mistakes/errors. But this waiting function will return false anyway if
3477
- * the number of polls done exceeds the max allowed.
3478
- *
3479
- * 2. if we just wait for the currently active calculation id to be finished then we return true
3480
- * when we notice that the current active id differs from the original passed into this function.
3481
- */
3482
- return Promise.resolve(true);
3483
- } else {
3484
- /* The original calculation id is still the active one, so we are scheduling a new attempt to check
3485
- * whether the active calculation id changed or not in timeoutDuration milliseconds.
3486
- */
3487
- var it = _this6;
3488
- return Promise.resolve(new Promise(function (resolve, reject) {
3489
- setTimeout(function () {
3490
- try {
3491
- resolve(it._waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex + 1));
3492
- } catch (e) {
3493
- reject(e);
3494
- }
3495
- }, _this6.timeoutDuration);
3496
- }));
3497
- }
3498
- } catch (e) {
3499
- Logger.logError(e, "_waitForCalculationIdToFinish", "Failed to wait for active calculation id change.");
3500
- return Promise.resolve(false);
3501
- }
3502
- } catch (e) {
3503
- return Promise.reject(e);
3504
- }
3505
- };
3506
- return ManagerOfRequestsToTheSameResource;
3507
- }();
3508
-
3509
- // TODO: [refactoring, low] Consider removing this logic task_id=c360f2af75764bde8badd9ff1cc00d48
3510
- var ConcurrentCalculationsMetadataHolder = /*#__PURE__*/function () {
3511
- function ConcurrentCalculationsMetadataHolder() {
3512
- this._calculations = {};
3513
- }
3514
- var _proto = ConcurrentCalculationsMetadataHolder.prototype;
3515
- _proto.startCalculation = function startCalculation(domain, calculationsHistoryMaxLength) {
3516
- if (calculationsHistoryMaxLength === void 0) {
3517
- calculationsHistoryMaxLength = 100;
3518
- }
3519
- if (!this._calculations[domain]) {
3520
- this._calculations[domain] = [];
3521
- }
3522
- if (this._calculations[domain].length > calculationsHistoryMaxLength) {
3523
- this._calculations[domain] = this._calculations[domain].slice(Math.round(calculationsHistoryMaxLength * 0.2));
3524
- }
3525
- var newCalculation = {
3526
- startTimestamp: Date.now(),
3527
- endTimestamp: null,
3528
- uuid: uuid.v4()
3529
- };
3530
- this._calculations[domain].push(newCalculation);
3531
- return newCalculation.uuid;
3532
- };
3533
- _proto.endCalculation = function endCalculation(domain, uuid, isFailed) {
3534
- if (isFailed === void 0) {
3535
- isFailed = false;
3536
- }
3537
- try {
3538
- var _calculation$endTimes, _calculation$startTim, _calculation$uuid;
3539
- var calculation = this._calculations[domain].find(function (calculation) {
3540
- return (calculation == null ? void 0 : calculation.uuid) === uuid;
3541
- });
3542
- if (calculation) {
3543
- calculation.endTimestamp = Date.now();
3544
- calculation.isFiled = isFailed;
3545
- }
3546
- var elapsed = ((((_calculation$endTimes = calculation == null ? void 0 : calculation.endTimestamp) != null ? _calculation$endTimes : 0) - ((_calculation$startTim = calculation == null ? void 0 : calculation.startTimestamp) != null ? _calculation$startTim : 0)) / 1000).toFixed(1);
3547
- Logger.log("endCalculation", elapsed + " ms: " + domain + "." + ((_calculation$uuid = calculation == null ? void 0 : calculation.uuid) != null ? _calculation$uuid : "").slice(0, 7));
3548
- return calculation;
3549
- } catch (e) {
3550
- Logger.logError(e, "endCalculation");
3154
+ Logger.logError(e, "endCalculation");
3551
3155
  }
3552
3156
  };
3553
3157
  _proto.isCalculationLate = function isCalculationLate(domain, uuid) {
@@ -3673,7 +3277,7 @@
3673
3277
  * improve the reliability of a data retrieval.
3674
3278
  */
3675
3279
 
3676
- function _catch$4(body, recover) {
3280
+ function _catch$6(body, recover) {
3677
3281
  try {
3678
3282
  var result = body();
3679
3283
  } catch (e) {
@@ -3684,6 +3288,634 @@
3684
3288
  }
3685
3289
  return result;
3686
3290
  }
3291
+ function _settle$2(pact, state, value) {
3292
+ if (!pact.s) {
3293
+ if (value instanceof _Pact$2) {
3294
+ if (value.s) {
3295
+ if (state & 1) {
3296
+ state = value.s;
3297
+ }
3298
+ value = value.v;
3299
+ } else {
3300
+ value.o = _settle$2.bind(null, pact, state);
3301
+ return;
3302
+ }
3303
+ }
3304
+ if (value && value.then) {
3305
+ value.then(_settle$2.bind(null, pact, state), _settle$2.bind(null, pact, 2));
3306
+ return;
3307
+ }
3308
+ pact.s = state;
3309
+ pact.v = value;
3310
+ var observer = pact.o;
3311
+ if (observer) {
3312
+ observer(pact);
3313
+ }
3314
+ }
3315
+ }
3316
+ var _Pact$2 = /*#__PURE__*/function () {
3317
+ function _Pact() {}
3318
+ _Pact.prototype.then = function (onFulfilled, onRejected) {
3319
+ var result = new _Pact();
3320
+ var state = this.s;
3321
+ if (state) {
3322
+ var callback = state & 1 ? onFulfilled : onRejected;
3323
+ if (callback) {
3324
+ try {
3325
+ _settle$2(result, 1, callback(this.v));
3326
+ } catch (e) {
3327
+ _settle$2(result, 2, e);
3328
+ }
3329
+ return result;
3330
+ } else {
3331
+ return this;
3332
+ }
3333
+ }
3334
+ this.o = function (_this) {
3335
+ try {
3336
+ var value = _this.v;
3337
+ if (_this.s & 1) {
3338
+ _settle$2(result, 1, onFulfilled ? onFulfilled(value) : value);
3339
+ } else if (onRejected) {
3340
+ _settle$2(result, 1, onRejected(value));
3341
+ } else {
3342
+ _settle$2(result, 2, value);
3343
+ }
3344
+ } catch (e) {
3345
+ _settle$2(result, 2, e);
3346
+ }
3347
+ };
3348
+ return result;
3349
+ };
3350
+ return _Pact;
3351
+ }();
3352
+ function _isSettledPact$2(thenable) {
3353
+ return thenable instanceof _Pact$2 && thenable.s & 1;
3354
+ }
3355
+ function _for$1(test, update, body) {
3356
+ var stage;
3357
+ for (;;) {
3358
+ var shouldContinue = test();
3359
+ if (_isSettledPact$2(shouldContinue)) {
3360
+ shouldContinue = shouldContinue.v;
3361
+ }
3362
+ if (!shouldContinue) {
3363
+ return result;
3364
+ }
3365
+ if (shouldContinue.then) {
3366
+ stage = 0;
3367
+ break;
3368
+ }
3369
+ var result = body();
3370
+ if (result && result.then) {
3371
+ if (_isSettledPact$2(result)) {
3372
+ result = result.s;
3373
+ } else {
3374
+ stage = 1;
3375
+ break;
3376
+ }
3377
+ }
3378
+ if (update) {
3379
+ var updateValue = update();
3380
+ if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
3381
+ stage = 2;
3382
+ break;
3383
+ }
3384
+ }
3385
+ }
3386
+ var pact = new _Pact$2();
3387
+ var reject = _settle$2.bind(null, pact, 2);
3388
+ (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
3389
+ return pact;
3390
+ function _resumeAfterBody(value) {
3391
+ result = value;
3392
+ do {
3393
+ if (update) {
3394
+ updateValue = update();
3395
+ if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
3396
+ updateValue.then(_resumeAfterUpdate).then(void 0, reject);
3397
+ return;
3398
+ }
3399
+ }
3400
+ shouldContinue = test();
3401
+ if (!shouldContinue || _isSettledPact$2(shouldContinue) && !shouldContinue.v) {
3402
+ _settle$2(pact, 1, result);
3403
+ return;
3404
+ }
3405
+ if (shouldContinue.then) {
3406
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3407
+ return;
3408
+ }
3409
+ result = body();
3410
+ if (_isSettledPact$2(result)) {
3411
+ result = result.v;
3412
+ }
3413
+ } while (!result || !result.then);
3414
+ result.then(_resumeAfterBody).then(void 0, reject);
3415
+ }
3416
+ function _resumeAfterTest(shouldContinue) {
3417
+ if (shouldContinue) {
3418
+ result = body();
3419
+ if (result && result.then) {
3420
+ result.then(_resumeAfterBody).then(void 0, reject);
3421
+ } else {
3422
+ _resumeAfterBody(result);
3423
+ }
3424
+ } else {
3425
+ _settle$2(pact, 1, result);
3426
+ }
3427
+ }
3428
+ function _resumeAfterUpdate() {
3429
+ if (shouldContinue = test()) {
3430
+ if (shouldContinue.then) {
3431
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3432
+ } else {
3433
+ _resumeAfterTest(shouldContinue);
3434
+ }
3435
+ } else {
3436
+ _settle$2(pact, 1, result);
3437
+ }
3438
+ }
3439
+ }
3440
+ function _finallyRethrows$1(body, finalizer) {
3441
+ try {
3442
+ var result = body();
3443
+ } catch (e) {
3444
+ return finalizer(true, e);
3445
+ }
3446
+ if (result && result.then) {
3447
+ return result.then(finalizer.bind(null, false), finalizer.bind(null, true));
3448
+ }
3449
+ return finalizer(false, result);
3450
+ }
3451
+ function _do(body, test) {
3452
+ var awaitBody;
3453
+ do {
3454
+ var result = body();
3455
+ if (result && result.then) {
3456
+ if (_isSettledPact$2(result)) {
3457
+ result = result.v;
3458
+ } else {
3459
+ awaitBody = true;
3460
+ break;
3461
+ }
3462
+ }
3463
+ var shouldContinue = test();
3464
+ if (_isSettledPact$2(shouldContinue)) {
3465
+ shouldContinue = shouldContinue.v;
3466
+ }
3467
+ if (!shouldContinue) {
3468
+ return result;
3469
+ }
3470
+ } while (!shouldContinue.then);
3471
+ var pact = new _Pact$2();
3472
+ var reject = _settle$2.bind(null, pact, 2);
3473
+ (awaitBody ? result.then(_resumeAfterBody) : shouldContinue.then(_resumeAfterTest)).then(void 0, reject);
3474
+ return pact;
3475
+ function _resumeAfterBody(value) {
3476
+ result = value;
3477
+ for (;;) {
3478
+ shouldContinue = test();
3479
+ if (_isSettledPact$2(shouldContinue)) {
3480
+ shouldContinue = shouldContinue.v;
3481
+ }
3482
+ if (!shouldContinue) {
3483
+ break;
3484
+ }
3485
+ if (shouldContinue.then) {
3486
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3487
+ return;
3488
+ }
3489
+ result = body();
3490
+ if (result && result.then) {
3491
+ if (_isSettledPact$2(result)) {
3492
+ result = result.v;
3493
+ } else {
3494
+ result.then(_resumeAfterBody).then(void 0, reject);
3495
+ return;
3496
+ }
3497
+ }
3498
+ }
3499
+ _settle$2(pact, 1, result);
3500
+ }
3501
+ function _resumeAfterTest(shouldContinue) {
3502
+ if (shouldContinue) {
3503
+ do {
3504
+ result = body();
3505
+ if (result && result.then) {
3506
+ if (_isSettledPact$2(result)) {
3507
+ result = result.v;
3508
+ } else {
3509
+ result.then(_resumeAfterBody).then(void 0, reject);
3510
+ return;
3511
+ }
3512
+ }
3513
+ shouldContinue = test();
3514
+ if (_isSettledPact$2(shouldContinue)) {
3515
+ shouldContinue = shouldContinue.v;
3516
+ }
3517
+ if (!shouldContinue) {
3518
+ _settle$2(pact, 1, result);
3519
+ return;
3520
+ }
3521
+ } while (!shouldContinue.then);
3522
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3523
+ } else {
3524
+ _settle$2(pact, 1, result);
3525
+ }
3526
+ }
3527
+ }
3528
+ function _forTo$1(array, body, check) {
3529
+ var i = -1,
3530
+ pact,
3531
+ reject;
3532
+ function _cycle(result) {
3533
+ try {
3534
+ while (++i < array.length && (!check || !check())) {
3535
+ result = body(i);
3536
+ if (result && result.then) {
3537
+ if (_isSettledPact$2(result)) {
3538
+ result = result.v;
3539
+ } else {
3540
+ result.then(_cycle, reject || (reject = _settle$2.bind(null, pact = new _Pact$2(), 2)));
3541
+ return;
3542
+ }
3543
+ }
3544
+ }
3545
+ if (pact) {
3546
+ _settle$2(pact, 1, result);
3547
+ } else {
3548
+ pact = result;
3549
+ }
3550
+ } catch (e) {
3551
+ _settle$2(pact || (pact = new _Pact$2()), 2, e);
3552
+ }
3553
+ }
3554
+ _cycle();
3555
+ return pact;
3556
+ }
3557
+ var RobustExternalAPICallerService = /*#__PURE__*/function () {
3558
+ RobustExternalAPICallerService.getStats = function getStats() {
3559
+ return this.statsCollector.getStats();
3560
+ }
3561
+
3562
+ /**
3563
+ * @param bio {string} service name for logging
3564
+ * @param providersData {ExternalApiProvider[]} array of providers
3565
+ * @param [logger] {function} function to be used for logging
3566
+ */;
3567
+ function RobustExternalAPICallerService(bio, providersData, logger) {
3568
+ providersData.forEach(function (provider) {
3569
+ if (!provider.endpoint && provider.endpoint !== "" || !provider.httpMethod) {
3570
+ throw new Error("Wrong format of providers data for: " + JSON.stringify(provider));
3571
+ }
3572
+ });
3573
+
3574
+ // We add niceFactor - just number to order the providers array by. It is helpful to call
3575
+ // less robust APIs only if more robust fails
3576
+ this.providers = providersData;
3577
+ providersData.forEach(function (provider) {
3578
+ return provider.resetNiceFactor();
3579
+ });
3580
+ this.bio = bio;
3581
+ this._logger = function () {
3582
+ Logger.logError.apply(Logger, [].slice.call(arguments));
3583
+ };
3584
+ }
3585
+ var _proto = RobustExternalAPICallerService.prototype;
3586
+ /**
3587
+ * Performs data retrieval from external APIs. Tries providers till the data is retrieved.
3588
+ *
3589
+ * @param parametersValues {array} array of values of the parameters for URL query string [and/or body]
3590
+ * @param timeoutMS {number} http timeout to wait for response. If provider has its specific timeout value then it is used
3591
+ * @param [cancelToken] {object|undefined} axios token to force-cancel requests from high-level code
3592
+ * @param [attemptsCount] {number|undefined} number of attempts to be performed
3593
+ * @param [doNotFailForNowData] {boolean|undefined} pass true if you do not want us to throw an error if we retrieved null data from all the providers
3594
+ * @return {Promise<any>} resolving to retrieved data (or array of results if specific provider requires
3595
+ * several requests. NOTE: we flatten nested arrays - results of each separate request done for the specific provider)
3596
+ * @throws Error if requests to all providers are failed
3597
+ */
3598
+ _proto.callExternalAPI = function callExternalAPI(parametersValues, timeoutMS, cancelToken, attemptsCount, doNotFailForNowData) {
3599
+ if (parametersValues === void 0) {
3600
+ parametersValues = [];
3601
+ }
3602
+ if (timeoutMS === void 0) {
3603
+ timeoutMS = 3500;
3604
+ }
3605
+ if (cancelToken === void 0) {
3606
+ cancelToken = null;
3607
+ }
3608
+ if (attemptsCount === void 0) {
3609
+ attemptsCount = 1;
3610
+ }
3611
+ if (doNotFailForNowData === void 0) {
3612
+ doNotFailForNowData = false;
3613
+ }
3614
+ try {
3615
+ var _this = this;
3616
+ var result;
3617
+ var calculationUuid = concurrentCalculationsMetadataHolder.startCalculation(_this.bio);
3618
+ return Promise.resolve(_finallyRethrows$1(function () {
3619
+ return _catch$6(function () {
3620
+ function _temp5() {
3621
+ var _result2, _result3;
3622
+ if (((_result2 = result) == null ? void 0 : _result2.data) == null) {
3623
+ // TODO: [feature, moderate] looks like we should not fail for null data as it is strange - the provider will fail when processing data internally
3624
+ var error = new Error("Failed to retrieve data. It means all attempts have been failed. DEV: add more attempts to this data retrieval");
3625
+ if (!doNotFailForNowData) {
3626
+ throw error;
3627
+ } else {
3628
+ _this._logger(error, _this.bio + ".callExternalAPI");
3629
+ }
3630
+ }
3631
+ return (_result3 = result) == null ? void 0 : _result3.data;
3632
+ }
3633
+ var i = 0;
3634
+ var _temp4 = _for$1(function () {
3635
+ var _result4, _result5;
3636
+ return (i < attemptsCount || !!((_result4 = result) != null && _result4.shouldBeForceRetried)) && ((_result5 = result) == null ? void 0 : _result5.data) == null;
3637
+ }, function () {
3638
+ return ++i;
3639
+ }, function () {
3640
+ /**
3641
+ * We use rpsFactor to improve re-attempting to call the providers if the last attempt resulted with
3642
+ * the fail due to abused RPSes of some (most part of) providers.
3643
+ * The _performCallAttempt in such a case will return increased rpsFactor inside the result object.
3644
+ */
3645
+ var rpsFactor = result ? result.rpsFactor : RobustExternalAPICallerService.defaultRPSFactor;
3646
+ result = null;
3647
+ var _temp3 = _catch$6(function () {
3648
+ function _temp2() {
3649
+ var _result$errors;
3650
+ if ((_result$errors = result.errors) != null && _result$errors.length) {
3651
+ var errors = result.errors;
3652
+ _this._logger(new Error("Failed at attempt " + i + ". " + errors.length + " errors. Messages: " + safeStringify(errors.map(function (error) {
3653
+ return error.message;
3654
+ })) + ": " + safeStringify(errors) + "."), _this.bio + ".callExternalAPI", "", true);
3655
+ }
3656
+ }
3657
+ var _temp = function (_result6) {
3658
+ if (i === 0 && !((_result6 = result) != null && _result6.shouldBeForceRetried)) {
3659
+ return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt) {
3660
+ result = _this$_performCallAtt;
3661
+ });
3662
+ } else {
3663
+ var maxRps = Math.max.apply(Math, _this.providers.map(function (provider) {
3664
+ var _provider$getRps;
3665
+ return (_provider$getRps = provider.getRps()) != null ? _provider$getRps : 0;
3666
+ }));
3667
+ var waitingTimeMs = maxRps ? 1000 / (maxRps / rpsFactor) : 0;
3668
+ return Promise.resolve(new Promise(function (resolve, reject) {
3669
+ setTimeout(function () {
3670
+ try {
3671
+ var _temp6 = _catch$6(function () {
3672
+ return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt2) {
3673
+ resolve(_this$_performCallAtt2);
3674
+ });
3675
+ }, function (e) {
3676
+ reject(e);
3677
+ });
3678
+ return Promise.resolve(_temp6 && _temp6.then ? _temp6.then(function () {}) : void 0);
3679
+ } catch (e) {
3680
+ return Promise.reject(e);
3681
+ }
3682
+ }, waitingTimeMs);
3683
+ })).then(function (_Promise) {
3684
+ result = _Promise;
3685
+ });
3686
+ }
3687
+ }();
3688
+ return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
3689
+ }, function (e) {
3690
+ _this._logger(e, _this.bio + ".callExternalAPI", "Failed to perform external providers calling");
3691
+ });
3692
+ if (_temp3 && _temp3.then) return _temp3.then(function () {});
3693
+ });
3694
+ return _temp4 && _temp4.then ? _temp4.then(_temp5) : _temp5(_temp4);
3695
+ }, function (e) {
3696
+ improveAndRethrow(e, _this.bio + ".callExternalAPI");
3697
+ });
3698
+ }, function (_wasThrown, _result) {
3699
+ concurrentCalculationsMetadataHolder.endCalculation(_this.bio, calculationUuid);
3700
+ if (_wasThrown) throw _result;
3701
+ return _result;
3702
+ }));
3703
+ } catch (e) {
3704
+ return Promise.reject(e);
3705
+ }
3706
+ };
3707
+ _proto._performCallAttempt = function _performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData) {
3708
+ try {
3709
+ var _temp15 = function _temp15() {
3710
+ var _data;
3711
+ // If we are declining more than 50% of providers (by exceeding RPS) then we note that it better to retry the whole process of providers requesting
3712
+ var shouldBeForceRetried = data == null && countOfRequestsDeclinedByRps > Math.floor(providers.length * 0.5);
3713
+ var rpsMultiplier = shouldBeForceRetried ? RobustExternalAPICallerService.rpsMultiplier : 1;
3714
+ return {
3715
+ data: (_data = data) != null ? _data : null,
3716
+ shouldBeForceRetried: shouldBeForceRetried,
3717
+ rpsFactor: rpsFactor * rpsMultiplier,
3718
+ errors: errors
3719
+ };
3720
+ };
3721
+ var _this2 = this;
3722
+ var providers = _this2._reorderProvidersByNiceFactor();
3723
+ var data = undefined,
3724
+ providerIndex = 0,
3725
+ countOfRequestsDeclinedByRps = 0,
3726
+ errors = [];
3727
+ var _temp14 = _for$1(function () {
3728
+ return !data && providerIndex < providers.length;
3729
+ }, void 0, function () {
3730
+ var provider = providers[providerIndex];
3731
+ if (provider.isRpsExceeded()) {
3732
+ /**
3733
+ * Current provider's RPS is exceeded, so we try next provider. Also, we count such cases to make
3734
+ * a decision about the force-retry need.
3735
+ */
3736
+ ++providerIndex;
3737
+ ++countOfRequestsDeclinedByRps;
3738
+ return;
3739
+ }
3740
+ var _temp13 = _finallyRethrows$1(function () {
3741
+ return _catch$6(function () {
3742
+ var _provider$specificHea;
3743
+ function _temp12() {
3744
+ if (iterationsData.length) {
3745
+ if (httpMethods.length > 1) {
3746
+ data = provider.incorporateIterationsData(iterationsData);
3747
+ } else {
3748
+ data = iterationsData[0];
3749
+ }
3750
+ } else if (!doNotFailForNowData) {
3751
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), "Response data was null for some reason");
3752
+ punishProvider(provider);
3753
+ }
3754
+ }
3755
+ var axiosConfig = _extends({}, cancelToken ? {
3756
+ cancelToken: cancelToken
3757
+ } : {}, {
3758
+ timeout: provider.timeout || timeoutMS,
3759
+ headers: (_provider$specificHea = provider.specificHeaders) != null ? _provider$specificHea : {}
3760
+ });
3761
+ var httpMethods = Array.isArray(provider.httpMethod) ? provider.httpMethod : [provider.httpMethod];
3762
+ var iterationsData = [];
3763
+ var _temp11 = _forTo$1(httpMethods, function (subRequestIndex) {
3764
+ function _temp10() {
3765
+ var responsesDataForPages = responsesForPages.map(function (response) {
3766
+ return provider.getDataByResponse(response, parametersValues, subRequestIndex, iterationsData);
3767
+ });
3768
+ var allData = responsesDataForPages;
3769
+ if (Array.isArray(responsesDataForPages[0])) {
3770
+ allData = responsesDataForPages.flat();
3771
+ } else if (responsesDataForPages.length === 1) {
3772
+ allData = responsesDataForPages[0];
3773
+ }
3774
+ iterationsData.push(allData);
3775
+ }
3776
+ var query = provider.composeQueryString(parametersValues, subRequestIndex);
3777
+ var endpoint = "" + provider.endpoint + query;
3778
+ var axiosParams = [endpoint, axiosConfig];
3779
+ if (["post", "put", "patch"].find(function (method) {
3780
+ return method === httpMethods[subRequestIndex];
3781
+ })) {
3782
+ var _provider$composeBody;
3783
+ var body = (_provider$composeBody = provider.composeBody(parametersValues, subRequestIndex)) != null ? _provider$composeBody : null;
3784
+ axiosParams.splice(1, 0, body);
3785
+ }
3786
+ var pageNumber = 0;
3787
+ var responsesForPages = [];
3788
+ var hasNextPage = provider.doesSupportPagination();
3789
+ var _temp9 = _do(function () {
3790
+ function _temp8() {
3791
+ if (hasNextPage) {
3792
+ hasNextPage = !provider.checkWhetherResponseIsForLastPage(responsesForPages[pageNumber - 1], responsesForPages[pageNumber], pageNumber, subRequestIndex);
3793
+ }
3794
+ pageNumber++;
3795
+ }
3796
+ var _temp7 = function () {
3797
+ if (subRequestIndex === 0 && pageNumber === 0) {
3798
+ provider.actualizeLastCalledTimestamp();
3799
+ return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams))).then(function (_AxiosAdapter$call) {
3800
+ responsesForPages[pageNumber] = _AxiosAdapter$call;
3801
+ RobustExternalAPICallerService.statsCollector.externalServiceCalledWithoutError(provider.getApiGroupId());
3802
+ });
3803
+ } else {
3804
+ if (pageNumber > 0) {
3805
+ var actualizedParams = provider.changeQueryParametersForPageNumber(parametersValues, responsesForPages[pageNumber - 1], pageNumber, subRequestIndex);
3806
+ var _query = provider.composeQueryString(actualizedParams, subRequestIndex);
3807
+ axiosParams[0] = "" + provider.endpoint + _query;
3808
+ }
3809
+ /**
3810
+ * For second and more request we postpone each request to not exceed RPS
3811
+ * of current provider. We use rpsFactor to dynamically increase the rps to avoid
3812
+ * too frequent calls if we continue failing to retrieve the data due to RPS exceeding.
3813
+ * TODO: [dev] test RPS factor logic (units or integration)
3814
+ */
3815
+
3816
+ var waitingTimeMS = provider.getRps() ? 1000 / (provider.getRps() / rpsFactor) : 0;
3817
+ var postponeUntilRpsExceeded = function postponeUntilRpsExceeded(recursionLevel) {
3818
+ if (recursionLevel === void 0) {
3819
+ recursionLevel = 0;
3820
+ }
3821
+ try {
3822
+ return Promise.resolve(postponeExecution(function () {
3823
+ try {
3824
+ var _temp17 = function _temp17(_result8) {
3825
+ if (_exit) return _result8;
3826
+ provider.actualizeLastCalledTimestamp();
3827
+ return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams)));
3828
+ };
3829
+ var _exit;
3830
+ var maxCountOfPostponingAttempts = 2;
3831
+ var _temp16 = function () {
3832
+ if (provider.isRpsExceeded() && recursionLevel < maxCountOfPostponingAttempts) {
3833
+ return Promise.resolve(postponeUntilRpsExceeded(recursionLevel + 1)).then(function (_await$postponeUntilR) {
3834
+ _exit = 1;
3835
+ return _await$postponeUntilR;
3836
+ });
3837
+ }
3838
+ }();
3839
+ return Promise.resolve(_temp16 && _temp16.then ? _temp16.then(_temp17) : _temp17(_temp16));
3840
+ } catch (e) {
3841
+ return Promise.reject(e);
3842
+ }
3843
+ }, waitingTimeMS));
3844
+ } catch (e) {
3845
+ return Promise.reject(e);
3846
+ }
3847
+ };
3848
+ return Promise.resolve(postponeUntilRpsExceeded()).then(function (_postponeUntilRpsExce) {
3849
+ responsesForPages[pageNumber] = _postponeUntilRpsExce;
3850
+ });
3851
+ }
3852
+ }();
3853
+ return _temp7 && _temp7.then ? _temp7.then(_temp8) : _temp8(_temp7);
3854
+ }, function () {
3855
+ return !!hasNextPage;
3856
+ });
3857
+ return _temp9 && _temp9.then ? _temp9.then(_temp10) : _temp10(_temp9);
3858
+ });
3859
+ return _temp11 && _temp11.then ? _temp11.then(_temp12) : _temp12(_temp11);
3860
+ }, function (e) {
3861
+ punishProvider(provider);
3862
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), e == null ? void 0 : e.message);
3863
+ errors.push(e);
3864
+ });
3865
+ }, function (_wasThrown2, _result7) {
3866
+ providerIndex++;
3867
+ if (_wasThrown2) throw _result7;
3868
+ return _result7;
3869
+ });
3870
+ if (_temp13 && _temp13.then) return _temp13.then(function () {});
3871
+ });
3872
+ return Promise.resolve(_temp14 && _temp14.then ? _temp14.then(_temp15) : _temp15(_temp14));
3873
+ } catch (e) {
3874
+ return Promise.reject(e);
3875
+ }
3876
+ };
3877
+ _proto._reorderProvidersByNiceFactor = function _reorderProvidersByNiceFactor() {
3878
+ var providersCopy = [].concat(this.providers);
3879
+ return providersCopy.sort(function (p1, p2) {
3880
+ return p2.niceFactor - p1.niceFactor;
3881
+ });
3882
+ };
3883
+ return RobustExternalAPICallerService;
3884
+ }();
3885
+ RobustExternalAPICallerService.statsCollector = new ExternalServicesStatsCollector();
3886
+ RobustExternalAPICallerService.defaultRPSFactor = 1;
3887
+ RobustExternalAPICallerService.rpsMultiplier = 1.05;
3888
+ function punishProvider(provider) {
3889
+ provider.niceFactor = provider.niceFactor - 1;
3890
+ }
3891
+
3892
+ /**
3893
+ * This util helps to avoid duplicated calls to a shared resource.
3894
+ * It tracks is there currently active calculation for the specific cache id and make all other requests
3895
+ * with the same cache id waiting for this active calculation to be finished. When the calculation ends
3896
+ * the resolver allows all the waiting requesters to get the data from cache and start their own calculations.
3897
+ *
3898
+ * This class should be instantiated inside some other service where you need to request some resource concurrently.
3899
+ * Rules:
3900
+ * 1. When you need to make a request inside your main service call 'getCachedOrWaitForCachedOrAcquireLock'
3901
+ * on the instance of this class and await for the result. If the flag allowing to start calculation is true
3902
+ * then you can request data inside your main service. Otherwise you should use the cached data as an another
3903
+ * requester just finished the most resent requesting and there is actual data in the cache that
3904
+ * is returned to you here.
3905
+ * 1.1 Also you can acquire a lock directly if you don't want to get cached data. Use the corresponding method 'acquireLock'.
3906
+ *
3907
+ * 2. If you start requesting (when you successfully acquired the lock) then after receiving the result of your
3908
+ * requesting you should call the 'saveCachedData' so the retrieved data will appear in the cache.
3909
+ *
3910
+ * 3. If you successfully acquired the lock then you should after calling the 'saveCachedData' call
3911
+ * the 'releaseLock' - this is mandatory to release the lock and allow other requesters to perform their requests.
3912
+ * WARNING: If for any reason you forget to call this method then this class instance will wait perpetually for
3913
+ * the lock releasing and all your attempts to request the data will constantly fail. So usually call it
3914
+ * inside the 'finally' block.
3915
+ *
3916
+ * TODO: [tests, critical++] add unit tests - massively used logic and can produce sophisticated concurrency bugs
3917
+ */
3918
+
3687
3919
  function _settle$1(pact, state, value) {
3688
3920
  if (!pact.s) {
3689
3921
  if (value instanceof _Pact$1) {
@@ -3703,12 +3935,17 @@
3703
3935
  }
3704
3936
  pact.s = state;
3705
3937
  pact.v = value;
3706
- var observer = pact.o;
3938
+ const observer = pact.o;
3707
3939
  if (observer) {
3708
3940
  observer(pact);
3709
3941
  }
3710
3942
  }
3711
3943
  }
3944
+
3945
+ /**
3946
+ * Util class to control access to a resource when it can be called in parallel for the same result.
3947
+ * (E.g. getting today coins-fiat rates from some API).
3948
+ */
3712
3949
  var _Pact$1 = /*#__PURE__*/function () {
3713
3950
  function _Pact() {}
3714
3951
  _Pact.prototype.then = function (onFulfilled, onRejected) {
@@ -3771,517 +4008,574 @@
3771
4008
  break;
3772
4009
  }
3773
4010
  }
3774
- if (update) {
3775
- var updateValue = update();
3776
- if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3777
- stage = 2;
3778
- break;
3779
- }
4011
+ if (update) {
4012
+ var updateValue = update();
4013
+ if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
4014
+ stage = 2;
4015
+ break;
4016
+ }
4017
+ }
4018
+ }
4019
+ var pact = new _Pact$1();
4020
+ var reject = _settle$1.bind(null, pact, 2);
4021
+ (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
4022
+ return pact;
4023
+ function _resumeAfterBody(value) {
4024
+ result = value;
4025
+ do {
4026
+ if (update) {
4027
+ updateValue = update();
4028
+ if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
4029
+ updateValue.then(_resumeAfterUpdate).then(void 0, reject);
4030
+ return;
4031
+ }
4032
+ }
4033
+ shouldContinue = test();
4034
+ if (!shouldContinue || _isSettledPact$1(shouldContinue) && !shouldContinue.v) {
4035
+ _settle$1(pact, 1, result);
4036
+ return;
4037
+ }
4038
+ if (shouldContinue.then) {
4039
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
4040
+ return;
4041
+ }
4042
+ result = body();
4043
+ if (_isSettledPact$1(result)) {
4044
+ result = result.v;
4045
+ }
4046
+ } while (!result || !result.then);
4047
+ result.then(_resumeAfterBody).then(void 0, reject);
4048
+ }
4049
+ function _resumeAfterTest(shouldContinue) {
4050
+ if (shouldContinue) {
4051
+ result = body();
4052
+ if (result && result.then) {
4053
+ result.then(_resumeAfterBody).then(void 0, reject);
4054
+ } else {
4055
+ _resumeAfterBody(result);
4056
+ }
4057
+ } else {
4058
+ _settle$1(pact, 1, result);
4059
+ }
4060
+ }
4061
+ function _resumeAfterUpdate() {
4062
+ if (shouldContinue = test()) {
4063
+ if (shouldContinue.then) {
4064
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
4065
+ } else {
4066
+ _resumeAfterTest(shouldContinue);
4067
+ }
4068
+ } else {
4069
+ _settle$1(pact, 1, result);
4070
+ }
4071
+ }
4072
+ }
4073
+ function _catch$5(body, recover) {
4074
+ try {
4075
+ var result = body();
4076
+ } catch (e) {
4077
+ return recover(e);
4078
+ }
4079
+ if (result && result.then) {
4080
+ return result.then(void 0, recover);
4081
+ }
4082
+ return result;
4083
+ }
4084
+ var CacheAndConcurrentRequestsResolver = /*#__PURE__*/function () {
4085
+ /**
4086
+ * @param bio {string} unique identifier for the exact service
4087
+ * @param cache {Cache} cache
4088
+ * @param cacheTtl {number|null} time to live for cache ms. 0 or null means the cache cannot expire
4089
+ * @param [maxCallAttemptsToWaitForAlreadyRunningRequest=100] {number} number of request allowed to do waiting for
4090
+ * result before we fail the original request. Use custom value only if you need to make the attempts count
4091
+ * and polling interval changes.
4092
+ * @param [timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished=1000] {number}
4093
+ * timeout ms for polling for a result. if you change maxCallAttemptsToWaitForAlreadyRunningRequest
4094
+ * then this parameter maybe also require the custom value.
4095
+ * @param [removeExpiredCacheAutomatically=true] {boolean}
4096
+ */
4097
+ function CacheAndConcurrentRequestsResolver(bio, cache, cacheTtl, removeExpiredCacheAutomatically, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished) {
4098
+ if (removeExpiredCacheAutomatically === void 0) {
4099
+ removeExpiredCacheAutomatically = true;
4100
+ }
4101
+ if (maxCallAttemptsToWaitForAlreadyRunningRequest === void 0) {
4102
+ maxCallAttemptsToWaitForAlreadyRunningRequest = 100;
4103
+ }
4104
+ if (timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished === void 0) {
4105
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished = 1000;
4106
+ }
4107
+ if (cacheTtl != null && cacheTtl < timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished * 2) {
4108
+ /*
4109
+ * During the lifetime of this service e.g. if the data is being retrieved slowly we can get
4110
+ * RACE CONDITION when we constantly retrieve data and during retrieval it is expired, so we are trying
4111
+ * to retrieve it again and again.
4112
+ * We have a protection mechanism that we will wait no more than
4113
+ * maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
4114
+ * but this additional check is aimed to reduce potential loading time for some requests.
4115
+ */
4116
+ throw new Error("DEV: Wrong parameters passed to construct " + bio + " - TTL " + cacheTtl + " should be 2 times greater than " + timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
4117
+ }
4118
+ this._bio = bio;
4119
+ this._cache = cache;
4120
+ this._cacheTtlMs = cacheTtl != null ? cacheTtl : null;
4121
+ this._maxExecutionTimeMs = maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished;
4122
+ this._removeExpiredCacheAutomatically = removeExpiredCacheAutomatically;
4123
+ this._requestsManager = new ManagerOfRequestsToTheSameResource(bio, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
4124
+ }
4125
+
4126
+ /**
4127
+ * When using this service this is the major method you should call to get data by cache id.
4128
+ * This method checks is there cached data and ether
4129
+ * - returns you flag that you can start requesting data from the shared resource
4130
+ * - or if there is already started calculation waits until it is finished (removed from this service)
4131
+ * and returns you the retrieved data
4132
+ * - or just returns you the cached data
4133
+ *
4134
+ * 'canStartDataRetrieval' equal true means that the lock was acquired, and you should manually call 'saveCachedData'
4135
+ * if needed and then 'releaseLock' to mark this calculation as finished so other
4136
+ * requesters can take their share of the resource.
4137
+ *
4138
+ * @param cacheId {string}
4139
+ * @return {Promise<({
4140
+ * canStartDataRetrieval: true,
4141
+ * cachedData: any,
4142
+ * lockId: string
4143
+ * }|{
4144
+ * canStartDataRetrieval: false,
4145
+ * cachedData: any
4146
+ * })>}
4147
+ */
4148
+ var _proto = CacheAndConcurrentRequestsResolver.prototype;
4149
+ _proto.getCachedOrWaitForCachedOrAcquireLock = function getCachedOrWaitForCachedOrAcquireLock(cacheId) {
4150
+ try {
4151
+ var _this = this;
4152
+ return Promise.resolve(_catch$5(function () {
4153
+ function _temp2() {
4154
+ var _cached, _cached2;
4155
+ return calculationId ? {
4156
+ canStartDataRetrieval: true,
4157
+ cachedData: (_cached = cached) != null ? _cached : cachedDataBackupIsPresentButExpired,
4158
+ lockId: calculationId
4159
+ } : {
4160
+ canStartDataRetrieval: false,
4161
+ cachedData: (_cached2 = cached) != null ? _cached2 : cachedDataBackupIsPresentButExpired
4162
+ };
4163
+ }
4164
+ var startedAtTimestamp = Date.now();
4165
+ var cached = _this._cache.get(cacheId);
4166
+ var cachedDataBackupIsPresentButExpired = null;
4167
+ if (cached != null && !_this._removeExpiredCacheAutomatically) {
4168
+ var lastUpdateTimestamp = _this._cache.getLastUpdateTimestamp(cacheId);
4169
+ if ((lastUpdateTimestamp != null ? lastUpdateTimestamp : 0) + _this._cacheTtlMs < Date.now()) {
4170
+ /*
4171
+ * Here we are manually clearing 'cached' value retrieved from cache to force the data loading.
4172
+ * But we save its value first to the backup variable to be able to return this value if ongoing
4173
+ * requesting fails.
4174
+ */
4175
+ cachedDataBackupIsPresentButExpired = cached;
4176
+ cached = null;
4177
+ }
4178
+ }
4179
+ var calculationId = null;
4180
+ var isRetrievedCacheExpired = true;
4181
+ var isWaitingForActiveCalculationSucceeded;
4182
+ var weStillHaveSomeTimeToProceedExecution = true;
4183
+ var _temp = _for(function () {
4184
+ return calculationId == null && cached == null && !!isRetrievedCacheExpired && !!weStillHaveSomeTimeToProceedExecution;
4185
+ }, void 0, function () {
4186
+ return Promise.resolve(_this._requestsManager.startCalculationOrWaitForActiveToFinish(cacheId)).then(function (result) {
4187
+ calculationId = typeof result === "string" ? result : null;
4188
+ isWaitingForActiveCalculationSucceeded = typeof result === "boolean" ? result : null;
4189
+ cached = _this._cache.get(cacheId);
4190
+ isRetrievedCacheExpired = isWaitingForActiveCalculationSucceeded && cached == null;
4191
+ weStillHaveSomeTimeToProceedExecution = Date.now() - startedAtTimestamp < _this._maxExecutionTimeMs;
4192
+ });
4193
+ });
4194
+ return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
4195
+ }, function (e) {
4196
+ improveAndRethrow(e, _this._bio + ".getCachedOrWaitForCachedOrAcquireLock");
4197
+ }));
4198
+ } catch (e) {
4199
+ return Promise.reject(e);
4200
+ }
4201
+ }
4202
+ /**
4203
+ * Returns just the current cache value for the given id.
4204
+ * Doesn't wait for the active calculation, doesn't acquire lock, just retrieves the current cache as it is.
4205
+ *
4206
+ * @param cacheId {string}
4207
+ * @return {any}
4208
+ */
4209
+ ;
4210
+ _proto.getCached = function getCached(cacheId) {
4211
+ try {
4212
+ return this._cache.get(cacheId);
4213
+ } catch (e) {
4214
+ improveAndRethrow(e, "getCached");
4215
+ }
4216
+ };
4217
+ _proto._getTtl = function _getTtl() {
4218
+ return this._removeExpiredCacheAutomatically ? this._cacheTtlMs : null;
4219
+ }
4220
+
4221
+ /**
4222
+ * Directly acquires the lock despite on cached data availability.
4223
+ * So if this method returns result === true you can start the data retrieval.
4224
+ *
4225
+ * @param cacheId {string}
4226
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>}
4227
+ */;
4228
+ _proto.acquireLock = function acquireLock(cacheId) {
4229
+ try {
4230
+ var _this2 = this;
4231
+ return Promise.resolve(_catch$5(function () {
4232
+ return Promise.resolve(_this2._requestsManager.acquireLock(cacheId));
4233
+ }, function (e) {
4234
+ improveAndRethrow(e, "acquireLock");
4235
+ }));
4236
+ } catch (e) {
4237
+ return Promise.reject(e);
4238
+ }
4239
+ }
4240
+ /**
4241
+ * This method should be called only if you acquired a lock successfully.
4242
+ *
4243
+ * If the current lock id is not equal to the passed one the passed data will be ignored.
4244
+ * Or you can do the synchronous data merging on your side and pass the
4245
+ * wasDataMergedSynchronouslyWithMostRecentCacheState=true so your data will be stored
4246
+ * despite on the lockId.
4247
+ * WARNING: you should do this only if you are sure you perform the synchronous update.
4248
+ *
4249
+ * @param cacheId {string}
4250
+ * @param lockId {string}
4251
+ * @param data {any}
4252
+ * @param [sessionDependentData=true] {boolean}
4253
+ * @param [wasDataMergedSynchronouslyWithMostRecentCacheState=false]
4254
+ */
4255
+ ;
4256
+ _proto.saveCachedData = function saveCachedData(cacheId, lockId, data, sessionDependentData, wasDataMergedSynchronouslyWithMostRecentCacheState) {
4257
+ if (sessionDependentData === void 0) {
4258
+ sessionDependentData = true;
4259
+ }
4260
+ if (wasDataMergedSynchronouslyWithMostRecentCacheState === void 0) {
4261
+ wasDataMergedSynchronouslyWithMostRecentCacheState = false;
3780
4262
  }
3781
- }
3782
- var pact = new _Pact$1();
3783
- var reject = _settle$1.bind(null, pact, 2);
3784
- (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
3785
- return pact;
3786
- function _resumeAfterBody(value) {
3787
- result = value;
3788
- do {
3789
- if (update) {
3790
- updateValue = update();
3791
- if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3792
- updateValue.then(_resumeAfterUpdate).then(void 0, reject);
3793
- return;
4263
+ try {
4264
+ if (wasDataMergedSynchronouslyWithMostRecentCacheState || this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
4265
+ /* We save passed data only if the <caller> has the currently acquired lockId.
4266
+ * If the passed lockId is not the active one it means that other code cleared/stopped the lock
4267
+ * acquired by the <caller> recently due to some urgent/more prior changes.
4268
+ *
4269
+ * But we allow user to pass the 'wasDataMergedSynchronouslyWithMostRecentCacheState' flag
4270
+ * that tells us that the user had taken the most recent cache value and merged his new data
4271
+ * with that cached value (AFTER possibly performing async data retrieval). This means that we
4272
+ * can ignore the fact that his lockId is no more relevant and save the passed data
4273
+ * as it is synchronously merged with the most recent cached data. (Synchronously merged means that
4274
+ * the lost update cannot occur during the merge time as JS execute the synchronous functions\
4275
+ * till the end).
4276
+ */
4277
+ if (sessionDependentData) {
4278
+ this._cache.putSessionDependentData(cacheId, data, this._getTtl());
4279
+ } else {
4280
+ this._cache.put(cacheId, data, this._getTtl());
3794
4281
  }
3795
4282
  }
3796
- shouldContinue = test();
3797
- if (!shouldContinue || _isSettledPact$1(shouldContinue) && !shouldContinue.v) {
3798
- _settle$1(pact, 1, result);
3799
- return;
3800
- }
3801
- if (shouldContinue.then) {
3802
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3803
- return;
3804
- }
3805
- result = body();
3806
- if (_isSettledPact$1(result)) {
3807
- result = result.v;
3808
- }
3809
- } while (!result || !result.then);
3810
- result.then(_resumeAfterBody).then(void 0, reject);
3811
- }
3812
- function _resumeAfterTest(shouldContinue) {
3813
- if (shouldContinue) {
3814
- result = body();
3815
- if (result && result.then) {
3816
- result.then(_resumeAfterBody).then(void 0, reject);
3817
- } else {
3818
- _resumeAfterBody(result);
3819
- }
3820
- } else {
3821
- _settle$1(pact, 1, result);
4283
+ } catch (e) {
4284
+ improveAndRethrow(e, this._bio + ".saveCachedData");
3822
4285
  }
3823
4286
  }
3824
- function _resumeAfterUpdate() {
3825
- if (shouldContinue = test()) {
3826
- if (shouldContinue.then) {
3827
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3828
- } else {
3829
- _resumeAfterTest(shouldContinue);
4287
+
4288
+ /**
4289
+ * Should be called then and only then if you successfully acquired a lock with the lock id.
4290
+ *
4291
+ * @param cacheId {string}
4292
+ * @param lockId {string}
4293
+ */;
4294
+ _proto.releaseLock = function releaseLock(cacheId, lockId) {
4295
+ try {
4296
+ if (this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
4297
+ this._requestsManager.finishActiveCalculation(cacheId);
3830
4298
  }
3831
- } else {
3832
- _settle$1(pact, 1, result);
4299
+ } catch (e) {
4300
+ improveAndRethrow(e, this._bio + ".releaseLock");
3833
4301
  }
3834
4302
  }
3835
- }
3836
- function _finallyRethrows$1(body, finalizer) {
3837
- try {
3838
- var result = body();
3839
- } catch (e) {
3840
- return finalizer(true, e);
3841
- }
3842
- if (result && result.then) {
3843
- return result.then(finalizer.bind(null, false), finalizer.bind(null, true));
3844
- }
3845
- return finalizer(false, result);
3846
- }
3847
- function _do(body, test) {
3848
- var awaitBody;
3849
- do {
3850
- var result = body();
3851
- if (result && result.then) {
3852
- if (_isSettledPact$1(result)) {
3853
- result = result.v;
3854
- } else {
3855
- awaitBody = true;
3856
- break;
3857
- }
3858
- }
3859
- var shouldContinue = test();
3860
- if (_isSettledPact$1(shouldContinue)) {
3861
- shouldContinue = shouldContinue.v;
3862
- }
3863
- if (!shouldContinue) {
3864
- return result;
4303
+
4304
+ /**
4305
+ * Actualized currently present cached data by key. Applies the provided function to the cached data.
4306
+ *
4307
+ * @param cacheId {string} id of cache entry
4308
+ * @param synchronousCurrentCacheProcessor (function|null} synchronous function accepting cache entry. Should return
4309
+ * an object in following format:
4310
+ * {
4311
+ * isModified: boolean,
4312
+ * data: any
4313
+ * }
4314
+ * the flag signals whether data was changed during the processing or not
4315
+ * @param [sessionDependent=true] {boolean} whether to mark the cache entry as session-dependent
4316
+ */;
4317
+ _proto.actualizeCachedData = function actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent) {
4318
+ if (sessionDependent === void 0) {
4319
+ sessionDependent = true;
3865
4320
  }
3866
- } while (!shouldContinue.then);
3867
- var pact = new _Pact$1();
3868
- var reject = _settle$1.bind(null, pact, 2);
3869
- (awaitBody ? result.then(_resumeAfterBody) : shouldContinue.then(_resumeAfterTest)).then(void 0, reject);
3870
- return pact;
3871
- function _resumeAfterBody(value) {
3872
- result = value;
3873
- for (;;) {
3874
- shouldContinue = test();
3875
- if (_isSettledPact$1(shouldContinue)) {
3876
- shouldContinue = shouldContinue.v;
3877
- }
3878
- if (!shouldContinue) {
3879
- break;
3880
- }
3881
- if (shouldContinue.then) {
3882
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3883
- return;
3884
- }
3885
- result = body();
3886
- if (result && result.then) {
3887
- if (_isSettledPact$1(result)) {
3888
- result = result.v;
4321
+ try {
4322
+ var cached = this._cache.get(cacheId);
4323
+ var result = synchronousCurrentCacheProcessor(cached);
4324
+ if (result != null && result.isModified && (result == null ? void 0 : result.data) != null) {
4325
+ if (sessionDependent) {
4326
+ this._cache.putSessionDependentData(cacheId, result == null ? void 0 : result.data, this._getTtl());
3889
4327
  } else {
3890
- result.then(_resumeAfterBody).then(void 0, reject);
3891
- return;
4328
+ this._cache.put(cacheId, result == null ? void 0 : result.data, this._getTtl());
3892
4329
  }
4330
+
4331
+ /* Here we call the lock releasing to ensure the currently active calculation will be ignored.
4332
+ * This is needed to ensure no 'lost update'.
4333
+ * Lost update can occur if we change data in this method and after that some calculation finishes
4334
+ * having the earlier data as its base to calculate its data set result. And the earlier data
4335
+ * has no changes applied inside this method, so we will lose them.
4336
+ *
4337
+ * This is not so good solution: ideally, we should acquire lock before performing any data updating.
4338
+ * But the goal of this method is to provide an instant ability to update the cached data.
4339
+ * And if we start acquiring the lock here the data update can be postponed significantly.
4340
+ * And this kills the desired nature of this method.
4341
+ * So we better lose some data retrieval (means abusing the resource a bit) than lose
4342
+ * the instant update expected after this method execution.
4343
+ */
4344
+ this._requestsManager.finishActiveCalculation(cacheId);
3893
4345
  }
4346
+ } catch (e) {
4347
+ improveAndRethrow(e, this._bio + ".actualizeCachedData");
4348
+ }
4349
+ };
4350
+ _proto.invalidate = function invalidate(key) {
4351
+ this._cache.invalidate(key);
4352
+ this._requestsManager.finishActiveCalculation(key);
4353
+ };
4354
+ _proto.invalidateContaining = function invalidateContaining(keyPart) {
4355
+ this._cache.invalidateContaining(keyPart);
4356
+ this._requestsManager.finishAllActiveCalculations(keyPart);
4357
+ };
4358
+ _proto.markAsExpiredButDontRemove = function markAsExpiredButDontRemove(key) {
4359
+ if (this._removeExpiredCacheAutomatically) {
4360
+ this._cache.markCacheItemAsExpiredButDontRemove(key, this._cacheTtlMs);
4361
+ } else {
4362
+ this._cache.setLastUpdateTimestamp(key, Date.now() - this._cacheTtlMs - 1);
4363
+ }
4364
+ this._requestsManager.finishAllActiveCalculations(key);
4365
+ };
4366
+ return CacheAndConcurrentRequestsResolver;
4367
+ }();
4368
+ var ManagerOfRequestsToTheSameResource = /*#__PURE__*/function () {
4369
+ /**
4370
+ * @param bio {string} resource-related identifier for logging
4371
+ * @param [maxPollsCount=100] {number} max number of attempts to wait when waiting for a lock acquisition
4372
+ * @param [timeoutDuration=1000] {number} timeout between the polls for a lock acquisition
4373
+ */
4374
+ function ManagerOfRequestsToTheSameResource(bio, maxPollsCount, timeoutDuration) {
4375
+ if (maxPollsCount === void 0) {
4376
+ maxPollsCount = 100;
4377
+ }
4378
+ if (timeoutDuration === void 0) {
4379
+ timeoutDuration = 1000;
3894
4380
  }
3895
- _settle$1(pact, 1, result);
4381
+ this.bio = bio;
4382
+ this.maxPollsCount = maxPollsCount;
4383
+ this.timeoutDuration = timeoutDuration;
4384
+ this._activeCalculationsIds = new Map();
4385
+ this._nextCalculationIds = new Map();
3896
4386
  }
3897
- function _resumeAfterTest(shouldContinue) {
3898
- if (shouldContinue) {
3899
- do {
3900
- result = body();
3901
- if (result && result.then) {
3902
- if (_isSettledPact$1(result)) {
3903
- result = result.v;
3904
- } else {
3905
- result.then(_resumeAfterBody).then(void 0, reject);
3906
- return;
3907
- }
3908
- }
3909
- shouldContinue = test();
3910
- if (_isSettledPact$1(shouldContinue)) {
3911
- shouldContinue = shouldContinue.v;
3912
- }
3913
- if (!shouldContinue) {
3914
- _settle$1(pact, 1, result);
3915
- return;
4387
+
4388
+ /**
4389
+ * If there is no active calculation just creates uuid and returns it.
4390
+ * If there is active calculation waits until it removed from the active calculation uuid variable.
4391
+ *
4392
+ * @param requestHash {string}
4393
+ * @return {Promise<string|boolean>} returns uuid of new active calculation or true if waiting for active
4394
+ * calculation succeed or false if max attempts count exceeded
4395
+ */
4396
+ var _proto2 = ManagerOfRequestsToTheSameResource.prototype;
4397
+ _proto2.startCalculationOrWaitForActiveToFinish = function startCalculationOrWaitForActiveToFinish(requestHash) {
4398
+ try {
4399
+ var _exit;
4400
+ var _this3 = this;
4401
+ var _temp3 = _catch$5(function () {
4402
+ var activeCalculationIdForHash = _this3._activeCalculationsIds.get(requestHash);
4403
+ if (activeCalculationIdForHash == null) {
4404
+ var id = uuid.v4();
4405
+ _this3._activeCalculationsIds.set(requestHash, id);
4406
+ _exit = 1;
4407
+ return id;
3916
4408
  }
3917
- } while (!shouldContinue.then);
3918
- shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3919
- } else {
3920
- _settle$1(pact, 1, result);
4409
+ return Promise.resolve(_this3._waitForCalculationIdToFinish(requestHash, activeCalculationIdForHash, 0)).then(function (_await$_this3$_waitFo) {
4410
+ _exit = 1;
4411
+ return _await$_this3$_waitFo;
4412
+ });
4413
+ }, function (e) {
4414
+ Logger.logError(e, "startCalculationOrWaitForActiveToFinish_" + _this3.bio);
4415
+ });
4416
+ return Promise.resolve(_temp3 && _temp3.then ? _temp3.then(function (_result3) {
4417
+ return _exit ? _result3 : null;
4418
+ }) : _exit ? _temp3 : null);
4419
+ } catch (e) {
4420
+ return Promise.reject(e);
3921
4421
  }
3922
4422
  }
3923
- }
3924
- function _forTo$1(array, body, check) {
3925
- var i = -1,
3926
- pact,
3927
- reject;
3928
- function _cycle(result) {
4423
+ /**
4424
+ * Acquires lock to the resource by the provided hash.
4425
+ *
4426
+ * @param requestHash {string}
4427
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>} result is true if the lock is successfully
4428
+ * acquired, false if the max allowed time to wait for acquisition expired or any unexpected error occurs
4429
+ * during the waiting.
4430
+ */
4431
+ ;
4432
+ _proto2.acquireLock = function acquireLock(requestHash) {
3929
4433
  try {
3930
- while (++i < array.length && (!check || !check())) {
3931
- result = body(i);
3932
- if (result && result.then) {
3933
- if (_isSettledPact$1(result)) {
3934
- result = result.v;
3935
- } else {
3936
- result.then(_cycle, reject || (reject = _settle$1.bind(null, pact = new _Pact$1(), 2)));
3937
- return;
3938
- }
4434
+ var _this4 = this;
4435
+ return Promise.resolve(_catch$5(function () {
4436
+ var _this4$_nextCalculati;
4437
+ var activeId = _this4._activeCalculationsIds.get(requestHash);
4438
+ var nextId = uuid.v4();
4439
+ if (activeId == null) {
4440
+ _this4._activeCalculationsIds.set(requestHash, nextId);
4441
+ return {
4442
+ result: true,
4443
+ lockId: nextId
4444
+ };
3939
4445
  }
3940
- }
3941
- if (pact) {
3942
- _settle$1(pact, 1, result);
3943
- } else {
3944
- pact = result;
3945
- }
4446
+ var currentNext = (_this4$_nextCalculati = _this4._nextCalculationIds.get(requestHash)) != null ? _this4$_nextCalculati : [];
4447
+ currentNext.push(nextId);
4448
+ _this4._nextCalculationIds.set(requestHash, currentNext);
4449
+ return Promise.resolve(_this4._waitForCalculationIdToFinish(requestHash, activeId, 0, nextId)).then(function (waitingResult) {
4450
+ return {
4451
+ result: waitingResult,
4452
+ lockId: waitingResult ? nextId : undefined
4453
+ };
4454
+ });
4455
+ }, function (e) {
4456
+ improveAndRethrow(e, "acquireLock");
4457
+ }));
3946
4458
  } catch (e) {
3947
- _settle$1(pact || (pact = new _Pact$1()), 2, e);
4459
+ return Promise.reject(e);
3948
4460
  }
3949
4461
  }
3950
- _cycle();
3951
- return pact;
3952
- }
3953
- var RobustExternalAPICallerService = /*#__PURE__*/function () {
3954
- RobustExternalAPICallerService.getStats = function getStats() {
3955
- this.statsCollector.getStats();
3956
- }
3957
-
3958
- /**
3959
- * @param bio {string} service name for logging
3960
- * @param providersData {ExternalApiProvider[]} array of providers
3961
- * @param [logger] {function} function to be used for logging
3962
- */;
3963
- function RobustExternalAPICallerService(bio, providersData, logger) {
3964
- providersData.forEach(function (provider) {
3965
- if (!provider.endpoint && provider.endpoint !== "" || !provider.httpMethod) {
3966
- throw new Error("Wrong format of providers data for: " + JSON.stringify(provider));
3967
- }
3968
- });
3969
-
3970
- // We add niceFactor - just number to order the providers array by. It is helpful to call
3971
- // less robust APIs only if more robust fails
3972
- this.providers = providersData;
3973
- providersData.forEach(function (provider) {
3974
- return provider.resetNiceFactor();
3975
- });
3976
- this.bio = bio;
3977
- this._logger = Logger.logError;
3978
- }
3979
- var _proto = RobustExternalAPICallerService.prototype;
3980
4462
  /**
3981
- * Performs data retrieval from external APIs. Tries providers till the data is retrieved.
3982
- *
3983
- * @param parametersValues {array} array of values of the parameters for URL query string [and/or body]
3984
- * @param timeoutMS {number} http timeout to wait for response. If provider has its specific timeout value then it is used
3985
- * @param [cancelToken] {object|undefined} axios token to force-cancel requests from high-level code
3986
- * @param [attemptsCount] {number|undefined} number of attempts to be performed
3987
- * @param [doNotFailForNowData] {boolean|undefined} pass true if you do not want us to throw an error if we retrieved null data from all the providers
3988
- * @return {Promise<any>} resolving to retrieved data (or array of results if specific provider requires
3989
- * several requests. NOTE: we flatten nested arrays - results of each separate request done for the specific provider)
3990
- * @throws Error if requests to all providers are failed
4463
+ * Clears active calculation id.
4464
+ * WARNING: if you forget to call this method the start* one will perform maxPollsCount attempts before finishing
4465
+ * @param requestHash {string} hash of request. Helps to distinct the request for the same resource but
4466
+ * having different request parameters and hold a dedicated calculation id per this hash
3991
4467
  */
3992
- _proto.callExternalAPI = function callExternalAPI(parametersValues, timeoutMS, cancelToken, attemptsCount, doNotFailForNowData) {
3993
- if (parametersValues === void 0) {
3994
- parametersValues = [];
3995
- }
3996
- if (timeoutMS === void 0) {
3997
- timeoutMS = 3500;
4468
+ ;
4469
+ _proto2.finishActiveCalculation = function finishActiveCalculation(requestHash) {
4470
+ if (requestHash === void 0) {
4471
+ requestHash = "default";
3998
4472
  }
3999
- if (cancelToken === void 0) {
4000
- cancelToken = null;
4473
+ try {
4474
+ var _this$_nextCalculatio;
4475
+ this._activeCalculationsIds["delete"](requestHash);
4476
+ var next = (_this$_nextCalculatio = this._nextCalculationIds.get(requestHash)) != null ? _this$_nextCalculatio : [];
4477
+ if (next.length) {
4478
+ this._activeCalculationsIds.set(requestHash, next[0]);
4479
+ this._nextCalculationIds.set(requestHash, next.slice(1));
4480
+ }
4481
+ } catch (e) {
4482
+ improveAndRethrow(e, "finishActiveCalculation");
4001
4483
  }
4002
- if (attemptsCount === void 0) {
4003
- attemptsCount = 1;
4484
+ };
4485
+ _proto2.finishAllActiveCalculations = function finishAllActiveCalculations(keyPart) {
4486
+ var _this5 = this;
4487
+ if (keyPart === void 0) {
4488
+ keyPart = "";
4004
4489
  }
4005
- if (doNotFailForNowData === void 0) {
4006
- doNotFailForNowData = false;
4490
+ try {
4491
+ Array.from(this._activeCalculationsIds.keys()).forEach(function (hash) {
4492
+ if (typeof hash === "string" && new RegExp(keyPart).test(hash)) {
4493
+ _this5.finishActiveCalculation(hash);
4494
+ }
4495
+ });
4496
+ } catch (e) {
4497
+ improveAndRethrow(e, "finishAllActiveCalculations");
4007
4498
  }
4499
+ }
4500
+
4501
+ /**
4502
+ * @param requestHash {string}
4503
+ * @param lockId {string}
4504
+ * @return {boolean}
4505
+ */;
4506
+ _proto2.isTheLockActiveOne = function isTheLockActiveOne(requestHash, lockId) {
4008
4507
  try {
4009
- var _this = this;
4010
- var result;
4011
- var calculationUuid = concurrentCalculationsMetadataHolder.startCalculation(_this.bio);
4012
- return Promise.resolve(_finallyRethrows$1(function () {
4013
- return _catch$4(function () {
4014
- function _temp5() {
4015
- var _result2, _result3;
4016
- if (((_result2 = result) == null ? void 0 : _result2.data) == null) {
4017
- // TODO: [feature, moderate] looks like we should not fail for null data as it is strange - the provider will fail when processing data internally
4018
- var error = new Error("Failed to retrieve data. It means all attempts have been failed. DEV: add more attempts to this data retrieval");
4019
- if (!doNotFailForNowData) {
4020
- throw error;
4021
- } else {
4022
- _this._logger(error, _this.bio + ".callExternalAPI");
4023
- }
4024
- }
4025
- return (_result3 = result) == null ? void 0 : _result3.data;
4026
- }
4027
- var i = 0;
4028
- var _temp4 = _for(function () {
4029
- var _result4, _result5;
4030
- return (i < attemptsCount || !!((_result4 = result) != null && _result4.shouldBeForceRetried)) && ((_result5 = result) == null ? void 0 : _result5.data) == null;
4031
- }, function () {
4032
- return ++i;
4033
- }, function () {
4034
- /**
4035
- * We use rpsFactor to improve re-attempting to call the providers if the last attempt resulted with
4036
- * the fail due to abused RPSes of some (most part of) providers.
4037
- * The _performCallAttempt in such a case will return increased rpsFactor inside the result object.
4038
- */
4039
- var rpsFactor = result ? result.rpsFactor : RobustExternalAPICallerService.defaultRPSFactor;
4040
- result = null;
4041
- var _temp3 = _catch$4(function () {
4042
- function _temp2() {
4043
- var _result$errors;
4044
- if ((_result$errors = result.errors) != null && _result$errors.length) {
4045
- var errors = result.errors;
4046
- _this._logger(new Error("Failed at attempt " + i + ". " + errors.length + " errors. Messages: " + safeStringify(errors.map(function (error) {
4047
- return error.message;
4048
- })) + ": " + safeStringify(errors) + "."), _this.bio + ".callExternalAPI", "", true);
4049
- }
4050
- }
4051
- var _temp = function (_result6) {
4052
- if (i === 0 && !((_result6 = result) != null && _result6.shouldBeForceRetried)) {
4053
- return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt) {
4054
- result = _this$_performCallAtt;
4055
- });
4056
- } else {
4057
- var maxRps = Math.max.apply(Math, _this.providers.map(function (provider) {
4058
- var _provider$getRps;
4059
- return (_provider$getRps = provider.getRps()) != null ? _provider$getRps : 0;
4060
- }));
4061
- var waitingTimeMs = maxRps ? 1000 / (maxRps / rpsFactor) : 0;
4062
- return Promise.resolve(new Promise(function (resolve, reject) {
4063
- setTimeout(function () {
4064
- try {
4065
- var _temp6 = _catch$4(function () {
4066
- return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt2) {
4067
- resolve(_this$_performCallAtt2);
4068
- });
4069
- }, function (e) {
4070
- reject(e);
4071
- });
4072
- return Promise.resolve(_temp6 && _temp6.then ? _temp6.then(function () {}) : void 0);
4073
- } catch (e) {
4074
- return Promise.reject(e);
4075
- }
4076
- }, waitingTimeMs);
4077
- })).then(function (_Promise) {
4078
- result = _Promise;
4079
- });
4080
- }
4081
- }();
4082
- return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
4083
- }, function (e) {
4084
- _this._logger(e, _this.bio + ".callExternalAPI", "Failed to perform external providers calling");
4085
- });
4086
- if (_temp3 && _temp3.then) return _temp3.then(function () {});
4087
- });
4088
- return _temp4 && _temp4.then ? _temp4.then(_temp5) : _temp5(_temp4);
4089
- }, function (e) {
4090
- improveAndRethrow(e, _this.bio + ".callExternalAPI");
4091
- });
4092
- }, function (_wasThrown, _result) {
4093
- concurrentCalculationsMetadataHolder.endCalculation(_this.bio, calculationUuid);
4094
- if (_wasThrown) throw _result;
4095
- return _result;
4096
- }));
4508
+ return this._activeCalculationsIds.get(requestHash) === lockId;
4097
4509
  } catch (e) {
4098
- return Promise.reject(e);
4510
+ improveAndRethrow(e, "isTheLockActiveOne");
4511
+ }
4512
+ }
4513
+
4514
+ /**
4515
+ * @param requestHash {string}
4516
+ * @param activeCalculationId {string|null}
4517
+ * @param [attemptIndex=0] {number}
4518
+ * @param waitForCalculationId {string|null} if you want to wait for an exact id to appear as active then pass this parameter
4519
+ * @return {Promise<boolean>} true
4520
+ * - if the given calculation id is no more an active one
4521
+ * - or it is equal to waitForCalculationId
4522
+ * false
4523
+ * - if waiting period exceeds the max allowed waiting time or unexpected error occurs
4524
+ * @private
4525
+ */;
4526
+ _proto2._waitForCalculationIdToFinish = function _waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex, waitForCalculationId) {
4527
+ if (attemptIndex === void 0) {
4528
+ attemptIndex = 0;
4529
+ }
4530
+ if (waitForCalculationId === void 0) {
4531
+ waitForCalculationId = null;
4099
4532
  }
4100
- };
4101
- _proto._performCallAttempt = function _performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData) {
4102
4533
  try {
4103
- var _temp15 = function _temp15() {
4104
- var _data;
4105
- // If we are declining more than 50% of providers (by exceeding RPS) then we note that it better to retry the whole process of providers requesting
4106
- var shouldBeForceRetried = data == null && countOfRequestsDeclinedByRps > Math.floor(providers.length * 0.5);
4107
- var rpsMultiplier = shouldBeForceRetried ? RobustExternalAPICallerService.rpsMultiplier : 1;
4108
- return {
4109
- data: (_data = data) != null ? _data : null,
4110
- shouldBeForceRetried: shouldBeForceRetried,
4111
- rpsFactor: rpsFactor * rpsMultiplier,
4112
- errors: errors
4113
- };
4114
- };
4115
- var _this2 = this;
4116
- var providers = _this2._reorderProvidersByNiceFactor();
4117
- var data = undefined,
4118
- providerIndex = 0,
4119
- countOfRequestsDeclinedByRps = 0,
4120
- errors = [];
4121
- var _temp14 = _for(function () {
4122
- return !data && providerIndex < providers.length;
4123
- }, void 0, function () {
4124
- var provider = providers[providerIndex];
4125
- if (provider.isRpsExceeded()) {
4126
- /**
4127
- * Current provider's RPS is exceeded, so we try next provider. Also, we count such cases to make
4128
- * a decision about the force-retry need.
4129
- */
4130
- ++providerIndex;
4131
- ++countOfRequestsDeclinedByRps;
4132
- return;
4534
+ var _this6 = this;
4535
+ try {
4536
+ if (attemptIndex + 1 > _this6.maxPollsCount) {
4537
+ // Max number of polls for active calculation id change is achieved. So we return false.
4538
+ return Promise.resolve(false);
4133
4539
  }
4134
- var _temp13 = _finallyRethrows$1(function () {
4135
- return _catch$4(function () {
4136
- var _provider$specificHea;
4137
- function _temp12() {
4138
- if (iterationsData.length) {
4139
- if (httpMethods.length > 1) {
4140
- data = provider.incorporateIterationsData(iterationsData);
4141
- } else {
4142
- data = iterationsData[0];
4143
- }
4144
- } else if (!doNotFailForNowData) {
4145
- RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), "Response data was null for some reason");
4146
- punishProvider(provider);
4147
- }
4148
- }
4149
- var axiosConfig = _extends({}, cancelToken ? {
4150
- cancelToken: cancelToken
4151
- } : {}, {
4152
- timeout: provider.timeout || timeoutMS,
4153
- headers: (_provider$specificHea = provider.specificHeaders) != null ? _provider$specificHea : {}
4154
- });
4155
- var httpMethods = Array.isArray(provider.httpMethod) ? provider.httpMethod : [provider.httpMethod];
4156
- var iterationsData = [];
4157
- var _temp11 = _forTo$1(httpMethods, function (subRequestIndex) {
4158
- function _temp10() {
4159
- var responsesDataForPages = responsesForPages.map(function (response) {
4160
- return provider.getDataByResponse(response, parametersValues, subRequestIndex, iterationsData);
4161
- });
4162
- var allData = responsesDataForPages;
4163
- if (Array.isArray(responsesDataForPages[0])) {
4164
- allData = responsesDataForPages.flat();
4165
- } else if (responsesDataForPages.length === 1) {
4166
- allData = responsesDataForPages[0];
4167
- }
4168
- iterationsData.push(allData);
4169
- }
4170
- var query = provider.composeQueryString(parametersValues, subRequestIndex);
4171
- var endpoint = "" + provider.endpoint + query;
4172
- var axiosParams = [endpoint, axiosConfig];
4173
- if (["post", "put", "patch"].find(function (method) {
4174
- return method === httpMethods[subRequestIndex];
4175
- })) {
4176
- var _provider$composeBody;
4177
- var body = (_provider$composeBody = provider.composeBody(parametersValues, subRequestIndex)) != null ? _provider$composeBody : null;
4178
- axiosParams.splice(1, 0, body);
4540
+ var currentId = _this6._activeCalculationsIds.get(requestHash);
4541
+ if (waitForCalculationId == null ? currentId !== activeCalculationId : currentId === waitForCalculationId) {
4542
+ /* We return true depending on the usage of this function:
4543
+ * 1. if there is calculation id that we should wait for to become an active then we return true only
4544
+ * if this id becomes the active one.
4545
+ *
4546
+ * Theoretically we can fail to wait for the desired calculation id. This can be caused by wrong use of
4547
+ * this service or by any other mistakes/errors. But this waiting function will return false anyway if
4548
+ * the number of polls done exceeds the max allowed.
4549
+ *
4550
+ * 2. if we just wait for the currently active calculation id to be finished then we return true
4551
+ * when we notice that the current active id differs from the original passed into this function.
4552
+ */
4553
+ return Promise.resolve(true);
4554
+ } else {
4555
+ /* The original calculation id is still the active one, so we are scheduling a new attempt to check
4556
+ * whether the active calculation id changed or not in timeoutDuration milliseconds.
4557
+ */
4558
+ var it = _this6;
4559
+ return Promise.resolve(new Promise(function (resolve, reject) {
4560
+ setTimeout(function () {
4561
+ try {
4562
+ resolve(it._waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex + 1));
4563
+ } catch (e) {
4564
+ reject(e);
4179
4565
  }
4180
- var pageNumber = 0;
4181
- var responsesForPages = [];
4182
- var hasNextPage = provider.doesSupportPagination();
4183
- var _temp9 = _do(function () {
4184
- function _temp8() {
4185
- if (hasNextPage) {
4186
- hasNextPage = !provider.checkWhetherResponseIsForLastPage(responsesForPages[pageNumber - 1], responsesForPages[pageNumber], pageNumber, subRequestIndex);
4187
- }
4188
- pageNumber++;
4189
- }
4190
- var _temp7 = function () {
4191
- if (subRequestIndex === 0 && pageNumber === 0) {
4192
- provider.actualizeLastCalledTimestamp();
4193
- return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams))).then(function (_AxiosAdapter$call) {
4194
- responsesForPages[pageNumber] = _AxiosAdapter$call;
4195
- RobustExternalAPICallerService.statsCollector.externalServiceCalledWithoutError(provider.getApiGroupId());
4196
- });
4197
- } else {
4198
- if (pageNumber > 0) {
4199
- var actualizedParams = provider.changeQueryParametersForPageNumber(parametersValues, responsesForPages[pageNumber - 1], pageNumber, subRequestIndex);
4200
- var _query = provider.composeQueryString(actualizedParams, subRequestIndex);
4201
- axiosParams[0] = "" + provider.endpoint + _query;
4202
- }
4203
- /**
4204
- * For second and more request we postpone each request to not exceed RPS
4205
- * of current provider. We use rpsFactor to dynamically increase the rps to avoid
4206
- * too frequent calls if we continue failing to retrieve the data due to RPS exceeding.
4207
- * TODO: [dev] test RPS factor logic (units or integration)
4208
- */
4209
-
4210
- var waitingTimeMS = provider.getRps() ? 1000 / (provider.getRps() / rpsFactor) : 0;
4211
- var postponeUntilRpsExceeded = function postponeUntilRpsExceeded(recursionLevel) {
4212
- if (recursionLevel === void 0) {
4213
- recursionLevel = 0;
4214
- }
4215
- try {
4216
- return Promise.resolve(postponeExecution(function () {
4217
- try {
4218
- var _temp17 = function _temp17(_result8) {
4219
- if (_exit) return _result8;
4220
- provider.actualizeLastCalledTimestamp();
4221
- return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams)));
4222
- };
4223
- var _exit;
4224
- var maxCountOfPostponingAttempts = 2;
4225
- var _temp16 = function () {
4226
- if (provider.isRpsExceeded() && recursionLevel < maxCountOfPostponingAttempts) {
4227
- return Promise.resolve(postponeUntilRpsExceeded(recursionLevel + 1)).then(function (_await$postponeUntilR) {
4228
- _exit = 1;
4229
- return _await$postponeUntilR;
4230
- });
4231
- }
4232
- }();
4233
- return Promise.resolve(_temp16 && _temp16.then ? _temp16.then(_temp17) : _temp17(_temp16));
4234
- } catch (e) {
4235
- return Promise.reject(e);
4236
- }
4237
- }, waitingTimeMS));
4238
- } catch (e) {
4239
- return Promise.reject(e);
4240
- }
4241
- };
4242
- return Promise.resolve(postponeUntilRpsExceeded()).then(function (_postponeUntilRpsExce) {
4243
- responsesForPages[pageNumber] = _postponeUntilRpsExce;
4244
- });
4245
- }
4246
- }();
4247
- return _temp7 && _temp7.then ? _temp7.then(_temp8) : _temp8(_temp7);
4248
- }, function () {
4249
- return !!hasNextPage;
4250
- });
4251
- return _temp9 && _temp9.then ? _temp9.then(_temp10) : _temp10(_temp9);
4252
- });
4253
- return _temp11 && _temp11.then ? _temp11.then(_temp12) : _temp12(_temp11);
4254
- }, function (e) {
4255
- punishProvider(provider);
4256
- RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), e == null ? void 0 : e.message);
4257
- errors.push(e);
4258
- });
4259
- }, function (_wasThrown2, _result7) {
4260
- providerIndex++;
4261
- if (_wasThrown2) throw _result7;
4262
- return _result7;
4263
- });
4264
- if (_temp13 && _temp13.then) return _temp13.then(function () {});
4265
- });
4266
- return Promise.resolve(_temp14 && _temp14.then ? _temp14.then(_temp15) : _temp15(_temp14));
4566
+ }, _this6.timeoutDuration);
4567
+ }));
4568
+ }
4569
+ } catch (e) {
4570
+ Logger.logError(e, "_waitForCalculationIdToFinish", "Failed to wait for active calculation id change.");
4571
+ return Promise.resolve(false);
4572
+ }
4267
4573
  } catch (e) {
4268
4574
  return Promise.reject(e);
4269
4575
  }
4270
4576
  };
4271
- _proto._reorderProvidersByNiceFactor = function _reorderProvidersByNiceFactor() {
4272
- var providersCopy = [].concat(this.providers);
4273
- return providersCopy.sort(function (p1, p2) {
4274
- return p2.niceFactor - p1.niceFactor;
4275
- });
4276
- };
4277
- return RobustExternalAPICallerService;
4577
+ return ManagerOfRequestsToTheSameResource;
4278
4578
  }();
4279
- RobustExternalAPICallerService.statsCollector = new ExternalServicesStatsCollector();
4280
- RobustExternalAPICallerService.defaultRPSFactor = 1;
4281
- RobustExternalAPICallerService.rpsMultiplier = 1.05;
4282
- function punishProvider(provider) {
4283
- provider.niceFactor = provider.niceFactor - 1;
4284
- }
4285
4579
 
4286
4580
  /**
4287
4581
  * Extended edit of RobustExternalApiCallerService supporting cache and management of concurrent requests
@@ -4289,7 +4583,7 @@
4289
4583
  * TODO: [tests, critical] Massively used logic
4290
4584
  */
4291
4585
 
4292
- function _catch$3(body, recover) {
4586
+ function _catch$4(body, recover) {
4293
4587
  try {
4294
4588
  var result = body();
4295
4589
  } catch (e) {
@@ -4386,7 +4680,7 @@
4386
4680
  var cacheId;
4387
4681
  var result;
4388
4682
  return Promise.resolve(_finallyRethrows(function () {
4389
- return _catch$3(function () {
4683
+ return _catch$4(function () {
4390
4684
  cacheId = _this._calculateCacheId(parametersValues, customHashFunctionForParams);
4391
4685
  return Promise.resolve(_this._cahceAndRequestsResolver.getCachedOrWaitForCachedOrAcquireLock(cacheId)).then(function (_this$_cahceAndReques) {
4392
4686
  var _result2, _result4;
@@ -4446,14 +4740,111 @@
4446
4740
  customHashFunctionForParams = null;
4447
4741
  }
4448
4742
  try {
4449
- var hash = typeof customHashFunctionForParams === "function" ? customHashFunctionForParams(parametersValues) : !parametersValues ? "" : new Hashes__default["default"].SHA512().hex(safeStringify(parametersValues));
4450
- return this._provider.bio + "-" + hash;
4743
+ var hash = typeof customHashFunctionForParams === "function" ? customHashFunctionForParams(parametersValues) : !parametersValues ? "" : new Hashes__default["default"].SHA512().hex(safeStringify(parametersValues));
4744
+ return this._provider.bio + "-" + hash;
4745
+ } catch (e) {
4746
+ improveAndRethrow(e, this._provider.bio + "_calculateCacheId");
4747
+ }
4748
+ };
4749
+ return CachedRobustExternalApiCallerService;
4750
+ }();
4751
+
4752
+ function _catch$3(body, recover) {
4753
+ try {
4754
+ var result = body();
4755
+ } catch (e) {
4756
+ return recover(e);
4757
+ }
4758
+ if (result && result.then) {
4759
+ return result.then(void 0, recover);
4760
+ }
4761
+ return result;
4762
+ }
4763
+ var BigdatacloudIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider) {
4764
+ _inheritsLoose(BigdatacloudIpAddressProvider, _ExternalApiProvider);
4765
+ function BigdatacloudIpAddressProvider() {
4766
+ return _ExternalApiProvider.call(this, "https://api.bigdatacloud.net/data/client-ip", "get", 15000, ApiGroups.BIGDATACLOUD) || this;
4767
+ }
4768
+ var _proto = BigdatacloudIpAddressProvider.prototype;
4769
+ _proto.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4770
+ var _response$data;
4771
+ return (response == null ? void 0 : response.data) && ((_response$data = response.data) == null ? void 0 : _response$data.ipString);
4772
+ };
4773
+ return BigdatacloudIpAddressProvider;
4774
+ }(ExternalApiProvider);
4775
+ var TrackipIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider2) {
4776
+ _inheritsLoose(TrackipIpAddressProvider, _ExternalApiProvider2);
4777
+ function TrackipIpAddressProvider() {
4778
+ return _ExternalApiProvider2.call(this, "https://www.trackip.net/ip", "get", 15000, ApiGroups.TRACKIP) || this;
4779
+ }
4780
+ var _proto2 = TrackipIpAddressProvider.prototype;
4781
+ _proto2.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4782
+ return response == null ? void 0 : response.data;
4783
+ };
4784
+ return TrackipIpAddressProvider;
4785
+ }(ExternalApiProvider);
4786
+ var IpifyV6IpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider3) {
4787
+ _inheritsLoose(IpifyV6IpAddressProvider, _ExternalApiProvider3);
4788
+ function IpifyV6IpAddressProvider() {
4789
+ return _ExternalApiProvider3.call(this, "https://api6.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY) || this;
4790
+ }
4791
+ var _proto3 = IpifyV6IpAddressProvider.prototype;
4792
+ _proto3.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4793
+ var _response$data2;
4794
+ return (response == null ? void 0 : response.data) && ((_response$data2 = response.data) == null ? void 0 : _response$data2.ip);
4795
+ };
4796
+ return IpifyV6IpAddressProvider;
4797
+ }(ExternalApiProvider);
4798
+ var IpifyIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider4) {
4799
+ _inheritsLoose(IpifyIpAddressProvider, _ExternalApiProvider4);
4800
+ function IpifyIpAddressProvider() {
4801
+ return _ExternalApiProvider4.call(this, "https://api.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY) || this;
4802
+ }
4803
+ var _proto4 = IpifyIpAddressProvider.prototype;
4804
+ _proto4.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4805
+ var _response$data3;
4806
+ return (response == null ? void 0 : response.data) && ((_response$data3 = response.data) == null ? void 0 : _response$data3.ip);
4807
+ };
4808
+ return IpifyIpAddressProvider;
4809
+ }(ExternalApiProvider);
4810
+ var WhatismyipaddressIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider5) {
4811
+ _inheritsLoose(WhatismyipaddressIpAddressProvider, _ExternalApiProvider5);
4812
+ function WhatismyipaddressIpAddressProvider() {
4813
+ return _ExternalApiProvider5.call(this, "http://bot.whatismyipaddress.com/", "get", 15000, ApiGroups.WHATISMYIPADDRESS) || this;
4814
+ }
4815
+ var _proto5 = WhatismyipaddressIpAddressProvider.prototype;
4816
+ _proto5.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4817
+ return response == null ? void 0 : response.data;
4818
+ };
4819
+ return WhatismyipaddressIpAddressProvider;
4820
+ }(ExternalApiProvider);
4821
+ var IpAddressProvider = /*#__PURE__*/function () {
4822
+ function IpAddressProvider() {}
4823
+ /**
4824
+ * Returns current public IP address identified by one of external services.
4825
+ *
4826
+ * It is easier than manual identification and also (as ip needed for server side to check it) it saves us from
4827
+ * issues related to changes of infrastructure configurations (like adding proxies etc.) so we should not configure
4828
+ * anything on server side to get correct client's IP.
4829
+ *
4830
+ * @returns {Promise<String>} IP address
4831
+ * @throws {Error} if fails to retrieve IP address from all the services
4832
+ */
4833
+ IpAddressProvider.getClientIpAddress = function getClientIpAddress() {
4834
+ try {
4835
+ var _this = this;
4836
+ return Promise.resolve(_catch$3(function () {
4837
+ return Promise.resolve(_this.externalIPAddressAPICaller.callExternalAPICached([], 7000));
4838
+ }, function (e) {
4839
+ improveAndRethrow(e, "getClientIpAddress");
4840
+ }));
4451
4841
  } catch (e) {
4452
- improveAndRethrow(e, this._provider.bio + "_calculateCacheId");
4842
+ return Promise.reject(e);
4453
4843
  }
4454
4844
  };
4455
- return CachedRobustExternalApiCallerService;
4845
+ return IpAddressProvider;
4456
4846
  }();
4847
+ IpAddressProvider.externalIPAddressAPICaller = new CachedRobustExternalApiCallerService("externalIPAddressAPICaller", new Cache(EventBusInstance__default["default"]), [new BigdatacloudIpAddressProvider(), new TrackipIpAddressProvider(), new IpifyV6IpAddressProvider(), new IpifyIpAddressProvider(), new WhatismyipaddressIpAddressProvider()], 300000);
4457
4848
 
4458
4849
  /**
4459
4850
  * Utils class needed to perform cancelling of axios request inside some process.
@@ -4481,239 +4872,6 @@
4481
4872
  return CancelProcessing;
4482
4873
  }();
4483
4874
 
4484
- var ExternalApiProvider = /*#__PURE__*/function () {
4485
- /**
4486
- * Creates an instance of external api provider.
4487
- *
4488
- * If you need sub-request then use 'subRequestIndex' to check current request index in functions below.
4489
- * Also use array for 'httpMethod'.
4490
- *
4491
- * If the endpoint of dedicated provider has pagination then you should customize the behavior using
4492
- * "changeQueryParametersForPageNumber", "checkWhetherResponseIsForLastPage".
4493
- *
4494
- * We perform RPS counting all over the App to avoid blocking our clients due to abuses of the providers.
4495
- *
4496
- * @param endpoint {string} URL to the provider's endpoint. Note: you can customize it using composeQueryString
4497
- * @param [httpMethod] {string|string[]} one of "get", "post", "put", "patch", "delete" or an array of these values
4498
- * for request having sub-requests
4499
- * @param [timeout] {number} number of milliseconds to wait for the response
4500
- * @param [apiGroup] {ApiGroup} singleton object containing parameters of API group. Helpful when you use the same
4501
- * api for different providers to avoid hardcoding RPS inside each provider what can cause mistakes
4502
- * @param [specificHeaders] {Object} contains specific keys (headers) and values (their content) if needed for this provider
4503
- * @param [maxPageLength] {number} optional number of items per page if the request supports pagination
4504
- */
4505
- function ExternalApiProvider(endpoint, httpMethod, timeout, apiGroup, specificHeaders, maxPageLength) {
4506
- var _maxPageLength, _specificHeaders;
4507
- if (specificHeaders === void 0) {
4508
- specificHeaders = {};
4509
- }
4510
- if (maxPageLength === void 0) {
4511
- maxPageLength = Number.MAX_SAFE_INTEGER;
4512
- }
4513
- this.endpoint = endpoint;
4514
- this.httpMethod = httpMethod != null ? httpMethod : "get";
4515
- // TODO: [refactoring, critical] We have two timeouts for robust data retrieval - here and inside the service method call, need to remain the only
4516
- this.timeout = timeout != null ? timeout : 10000;
4517
- // TODO: [refactoring, critical] We need single place for all RPSes as we use them as hardcoded constants now inside different services
4518
- this.apiGroup = apiGroup;
4519
- this.maxPageLength = (_maxPageLength = maxPageLength) != null ? _maxPageLength : Number.MAX_SAFE_INTEGER;
4520
- this.niceFactor = 1;
4521
- this.specificHeaders = (_specificHeaders = specificHeaders) != null ? _specificHeaders : {};
4522
- }
4523
- var _proto = ExternalApiProvider.prototype;
4524
- _proto.getRps = function getRps() {
4525
- var _this$apiGroup$rps;
4526
- return (_this$apiGroup$rps = this.apiGroup.rps) != null ? _this$apiGroup$rps : 2;
4527
- };
4528
- _proto.isRpsExceeded = function isRpsExceeded() {
4529
- return this.apiGroup.isRpsExceeded();
4530
- };
4531
- _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
4532
- this.apiGroup.actualizeLastCalledTimestamp();
4533
- };
4534
- _proto.getApiGroupId = function getApiGroupId() {
4535
- return this.apiGroup.id;
4536
- }
4537
-
4538
- /**
4539
- * Some endpoint can require several sub requests. Example is one request to get confirmed transactions
4540
- * and another request for unconfirmed transactions. You should override this method to return true for such requests.
4541
- *
4542
- * @return {boolean} true if this provider requires several requests to retrieve the data
4543
- */;
4544
- _proto.doesRequireSubRequests = function doesRequireSubRequests() {
4545
- return false;
4546
- }
4547
-
4548
- /**
4549
- * Some endpoint support pagination. Override this method if so and implement corresponding methods.
4550
- *
4551
- * @return {boolean} true if this provider requires several requests to retrieve the data
4552
- */;
4553
- _proto.doesSupportPagination = function doesSupportPagination() {
4554
- return false;
4555
- }
4556
-
4557
- /**
4558
- * Composes a query string to be added to the endpoint of this provider.
4559
- *
4560
- * @param params {any[]} params array passed to the RobustExternalAPICallerService
4561
- * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
4562
- * @returns {string} query string to be concatenated with endpoint
4563
- */;
4564
- _proto.composeQueryString = function composeQueryString(params, subRequestIndex) {
4565
- return "";
4566
- }
4567
-
4568
- /**
4569
- * Composes a body to be added to the request
4570
- *
4571
- * @param params {any[]} params array passed to the RobustExternalAPICallerService
4572
- * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
4573
- * @returns {string}
4574
- */;
4575
- _proto.composeBody = function composeBody(params, subRequestIndex) {
4576
- return "";
4577
- }
4578
-
4579
- /**
4580
- * Extracts data from the response and returns it
4581
- *
4582
- * @param response {Object} HTTP response returned by provider
4583
- * @param [params] {any[]} params array passed to the RobustExternalAPICallerService
4584
- * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
4585
- * @param iterationsData {any[]} array of data retrieved from previous sub-requests
4586
- * @returns {any}
4587
- */;
4588
- _proto.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4589
- return [];
4590
- }
4591
-
4592
- /**
4593
- * Function changing the query string according to page number and previous response
4594
- * Only for endpoints supporting pagination
4595
- *
4596
- * @param params {any[]} params array passed to the RobustExternalAPICallerService
4597
- * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
4598
- * @param pageNumber {number} new page number. We count from 0. You need to manually increment with 1 if your
4599
- * provider counts pages starting with 1
4600
- * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
4601
- * @returns {any[]}
4602
- */;
4603
- _proto.changeQueryParametersForPageNumber = function changeQueryParametersForPageNumber(params, previousResponse, pageNumber, subRequestIndex) {
4604
- return params;
4605
- }
4606
-
4607
- /**
4608
- * Function checking whether the response is for the last page to stop requesting for a next page.
4609
- * Only for endpoints supporting pagination.
4610
- *
4611
- * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
4612
- * @param currentResponse {Object} HTTP response returned by provider for current call (current page, next after the previous)
4613
- * @param currentPageNumber {number} current page number (for current response)
4614
- * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
4615
- * @returns {boolean}
4616
- */;
4617
- _proto.checkWhetherResponseIsForLastPage = function checkWhetherResponseIsForLastPage(previousResponse, currentResponse, currentPageNumber, subRequestIndex) {
4618
- return true;
4619
- }
4620
-
4621
- /**
4622
- * Resets the nice factor to default value
4623
- */;
4624
- _proto.resetNiceFactor = function resetNiceFactor() {
4625
- this.niceFactor = 1;
4626
- }
4627
-
4628
- /**
4629
- * Internal method used for requests requiring sub-requests.
4630
- *
4631
- * @param iterationsData {any[]} iterations data retrieved from getDataByResponse called per sub-request.
4632
- * @return {any} by default flatten the passed iterations data array. Should be redefined if you need another logic.
4633
- */;
4634
- _proto.incorporateIterationsData = function incorporateIterationsData(iterationsData) {
4635
- return iterationsData.flat();
4636
- };
4637
- return ExternalApiProvider;
4638
- }();
4639
-
4640
- /**
4641
- * Models a group of APIs provided by the same owner and used for different services in our app.
4642
- * It means we need to mention RPS several times for each usage and also have some holder of last call timestamp per
4643
- * api group. So this concept allows to use it for exact ExternalApiProvider and make sure that you use the same
4644
- * RPS value and make decisions on base of the same timestamp of last call to the API group owner.
4645
- */
4646
- var ApiGroup = /*#__PURE__*/function () {
4647
- function ApiGroup(id, rps, backendProxyIdGenerator) {
4648
- if (backendProxyIdGenerator === void 0) {
4649
- backendProxyIdGenerator = null;
4650
- }
4651
- this.id = id;
4652
- this.rps = rps;
4653
- this.lastCalledTimestamp = null;
4654
- this.backendProxyIdGenerator = backendProxyIdGenerator;
4655
- }
4656
- var _proto = ApiGroup.prototype;
4657
- _proto.isRpsExceeded = function isRpsExceeded() {
4658
- var _this$lastCalledTimes;
4659
- return ((_this$lastCalledTimes = this.lastCalledTimestamp) != null ? _this$lastCalledTimes : 0) + Math.floor(1000 / this.rps) > Date.now();
4660
- };
4661
- _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
4662
- this.lastCalledTimestamp = Date.now();
4663
- };
4664
- return ApiGroup;
4665
- }();
4666
- var ApiGroups = {
4667
- /**
4668
- * Currently we use free version of etherscan provider with 0.2 RPS. But we have API key with 100k requests free
4669
- * per month. So we can add it if not enough current RPS.
4670
- */
4671
- ETHERSCAN: new ApiGroup("etherscan", 0.17),
4672
- // Actually 0.2 but fails sometime, so we use smaller
4673
- ALCHEMY: new ApiGroup("alchemy", 0.3, function (networkKey) {
4674
- return "alchemy-" + networkKey;
4675
- }),
4676
- BLOCKSTREAM: new ApiGroup("blockstream", 0.2),
4677
- BLOCKCHAIN_INFO: new ApiGroup("blockchain.info", 1),
4678
- BLOCKNATIVE: new ApiGroup("blocknative", 0.5),
4679
- ETHGASSTATION: new ApiGroup("ethgasstation", 0.5),
4680
- TRONGRID: new ApiGroup("trongrid", 0.3, function (networkKey) {
4681
- return "trongrid-" + networkKey;
4682
- }),
4683
- TRONSCAN: new ApiGroup("tronscan", 0.3),
4684
- GETBLOCK: new ApiGroup("getblock", 0.3),
4685
- COINCAP: new ApiGroup("coincap", 0.5),
4686
- // 200 per minute without API key
4687
- COINGECKO: new ApiGroup("coingecko", 0.9),
4688
- // actually 0.13-0.5 according to the docs but we use smaller due to expirienced frequent abuses
4689
- MESSARI: new ApiGroup("messari", 0.2),
4690
- BTCCOM: new ApiGroup("btccom", 0.2),
4691
- BITAPS: new ApiGroup("bitaps", 0.25),
4692
- // Docs say that RPS is 3 but using it causes frequent 429 HTTP errors
4693
- CEX: new ApiGroup("cex", 0.5),
4694
- // Just assumption for RPS
4695
- BIGDATACLOUD: new ApiGroup("bigdatacloud", 1),
4696
- // Just assumption for RPS
4697
- TRACKIP: new ApiGroup("trackip", 1),
4698
- // Just assumption for RPS
4699
- IPIFY: new ApiGroup("ipify", 1),
4700
- // Just assumption for RPS
4701
- WHATISMYIPADDRESS: new ApiGroup("whatismyipaddress", 1),
4702
- // Just assumption for RPS
4703
- EXCHANGERATE: new ApiGroup("exchangerate", 1),
4704
- // Just assumption for RPS
4705
- FRANKFURTER: new ApiGroup("frankfurter", 1),
4706
- // Just assumption for RPS
4707
- BITGO: new ApiGroup("bitgo", 1),
4708
- // Just assumption for RPS
4709
- BITCOINER: new ApiGroup("bitcoiner", 1),
4710
- // Just assumption for RPS
4711
- BITCORE: new ApiGroup("bitcore", 1),
4712
- // Just assumption for RPS
4713
- // BLOCKCHAIR: new ApiGroup("blockchair", 0.04), // this provider require API key for commercial use (10usd 10000 reqs), we will add it later
4714
- MEMPOOL: new ApiGroup("mempool", 0.2) // Just assumption for RPS
4715
- };
4716
-
4717
4875
  var ExistingSwap =
4718
4876
  /**
4719
4877
  * @param swapId {string}
@@ -4722,6 +4880,7 @@
4722
4880
  * @param expiresAt {number}
4723
4881
  * @param confirmations {number}
4724
4882
  * @param rate {string}
4883
+ * @param fixed {boolean}
4725
4884
  * @param refundAddress {string}
4726
4885
  * @param payToAddress {string}
4727
4886
  * @param fromCoin {Coin}
@@ -4738,7 +4897,7 @@
4738
4897
  * @param toExtraId {string}
4739
4898
  * @param refundExtraId {string}
4740
4899
  */
4741
- function ExistingSwap(swapId, status, createdAt, expiresAt, confirmations, rate, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress,
4900
+ function ExistingSwap(swapId, status, createdAt, expiresAt, confirmations, rate, fixed, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress,
4742
4901
  // TODO: [refactoring, moderate] toAddress is not quite clear. How about recipientAddress? task_id=0815a111c99543b78d374217eadbde4f
4743
4902
  partner, fromExtraId, toExtraId, refundExtraId) {
4744
4903
  this.swapId = swapId;
@@ -4747,6 +4906,7 @@
4747
4906
  this.expiresAt = expiresAt;
4748
4907
  this.confirmations = confirmations;
4749
4908
  this.rate = rate;
4909
+ this.fixed = fixed;
4750
4910
  this.refundAddress = refundAddress;
4751
4911
  this.payToAddress = payToAddress;
4752
4912
  this.fromCoin = fromCoin;
@@ -4773,6 +4933,7 @@
4773
4933
  * @param expiresAt {number}
4774
4934
  * @param confirmations {number}
4775
4935
  * @param rate {string}
4936
+ * @param fixed {boolean}
4776
4937
  * @param refundAddress {string}
4777
4938
  * @param payToAddress {string}
4778
4939
  * @param fromCoin {Coin}
@@ -4793,9 +4954,9 @@
4793
4954
  * @param fiatCurrencyCode {string}
4794
4955
  * @param fiatCurrencyDecimals {number}
4795
4956
  */
4796
- function ExistingSwapWithFiatData(swapId, status, createdAt, expiresAt, confirmations, rate, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress, partner, fromExtraId, toExtraId, refundExtraId, fromAmountFiat, toAmountFiat, fiatCurrencyCode, fiatCurrencyDecimals) {
4957
+ function ExistingSwapWithFiatData(swapId, status, createdAt, expiresAt, confirmations, rate, fixed, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress, partner, fromExtraId, toExtraId, refundExtraId, fromAmountFiat, toAmountFiat, fiatCurrencyCode, fiatCurrencyDecimals) {
4797
4958
  var _this;
4798
- _this = _ExistingSwap.call(this, swapId, status, createdAt, expiresAt, confirmations, rate, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress, partner, fromExtraId, toExtraId, refundExtraId) || this;
4959
+ _this = _ExistingSwap.call(this, swapId, status, createdAt, expiresAt, confirmations, rate, fixed, refundAddress, payToAddress, fromCoin, fromAmount, fromTransactionId, fromTransactionLink, toCoin, toAmount, toTransactionId, toTransactionLink, toAddress, partner, fromExtraId, toExtraId, refundExtraId) || this;
4799
4960
  _this.fromAmountFiat = fromAmountFiat;
4800
4961
  _this.toAmountFiat = toAmountFiat;
4801
4962
  _this.fiatCurrencyCode = fiatCurrencyCode;
@@ -4812,7 +4973,7 @@
4812
4973
  * @return {ExistingSwapWithFiatData}
4813
4974
  */
4814
4975
  ExistingSwapWithFiatData.fromExistingSwap = function fromExistingSwap(existingSwap, fromAmountFiat, toAmountFiat, fiatCurrencyCode, fiatCurrencyDecimals) {
4815
- return new ExistingSwapWithFiatData(existingSwap.swapId, existingSwap.status, existingSwap.createdAt, existingSwap.expiresAt, existingSwap.confirmations, existingSwap.rate, existingSwap.refundAddress, existingSwap.payToAddress, existingSwap.fromCoin, existingSwap.fromAmount, existingSwap.fromTransactionId, existingSwap.fromTransactionLink, existingSwap.toCoin, existingSwap.toAmount, existingSwap.toTransactionId, existingSwap.toTransactionLink, existingSwap.toAddress, existingSwap.partner, existingSwap.fromExtraId, existingSwap.toExtraId, existingSwap.refundExtraId, fromAmountFiat, toAmountFiat, fiatCurrencyCode, fiatCurrencyDecimals);
4976
+ return new ExistingSwapWithFiatData(existingSwap.swapId, existingSwap.status, existingSwap.createdAt, existingSwap.expiresAt, existingSwap.confirmations, existingSwap.rate, existingSwap.fixed, existingSwap.refundAddress, existingSwap.payToAddress, existingSwap.fromCoin, existingSwap.fromAmount, existingSwap.fromTransactionId, existingSwap.fromTransactionLink, existingSwap.toCoin, existingSwap.toAmount, existingSwap.toTransactionId, existingSwap.toTransactionLink, existingSwap.toAddress, existingSwap.partner, existingSwap.fromExtraId, existingSwap.toExtraId, existingSwap.refundExtraId, fromAmountFiat, toAmountFiat, fiatCurrencyCode, fiatCurrencyDecimals);
4816
4977
  };
4817
4978
  return ExistingSwapWithFiatData;
4818
4979
  }(ExistingSwap);
@@ -4830,8 +4991,9 @@
4830
4991
  * @param max {string}
4831
4992
  * @param fiatMax {number}
4832
4993
  * @param durationMinutesRange {string}
4994
+ * @param fixed {boolean}
4833
4995
  */
4834
- function BaseSwapCreationInfo(fromCoin, toCoin, fromAmountCoins, toAmountCoins, rate, rawSwapData, min, fiatMin, max, fiatMax, durationMinutesRange) {
4996
+ function BaseSwapCreationInfo(fromCoin, toCoin, fromAmountCoins, toAmountCoins, rate, rawSwapData, min, fiatMin, max, fiatMax, durationMinutesRange, fixed) {
4835
4997
  this.fromCoin = fromCoin;
4836
4998
  this.toCoin = toCoin;
4837
4999
  this.fromAmountCoins = fromAmountCoins;
@@ -4843,6 +5005,7 @@
4843
5005
  this.max = max;
4844
5006
  this.fiatMax = fiatMax;
4845
5007
  this.durationMinutesRange = durationMinutesRange;
5008
+ this.fixed = fixed;
4846
5009
  };
4847
5010
 
4848
5011
  var SwapProvider = /*#__PURE__*/function () {
@@ -4941,6 +5104,7 @@
4941
5104
  * @param fromCoin {Coin}
4942
5105
  * @param toCoin {Coin}
4943
5106
  * @param amountCoins {string}
5107
+ * @param [fixed=false] {boolean|null} null means fixed or float doesn't matter
4944
5108
  * @param [fromCoinToUsdRate=null] pass if you want to increase the min amount returned
4945
5109
  * by provider with some fixed "insurance" amount to cover min amount fluctuations.
4946
5110
  * @return {Promise<({
@@ -4956,11 +5120,12 @@
4956
5120
  * greatestMax: (string|null),
4957
5121
  * rate: (string|null),
4958
5122
  * durationMinutesRange: string,
5123
+ * fixed: boolean,
4959
5124
  * [rawSwapData]: Object
4960
5125
  * })>}
4961
5126
  */
4962
5127
  ;
4963
- _proto.getSwapInfo = function getSwapInfo(fromCoin, toCoin, amountCoins, fromCoinToUsdRate) {
5128
+ _proto.getSwapInfo = function getSwapInfo(fromCoin, toCoin, amountCoins, fixed, fromCoinToUsdRate) {
4964
5129
  try {
4965
5130
  throw new Error("Not implemented in base");
4966
5131
  } catch (e) {
@@ -4968,7 +5133,7 @@
4968
5133
  }
4969
5134
  }
4970
5135
  /**
4971
- * For fail result we return one of SwapProvider.CREATION_FAIL_REASONS or SwapProvider.COMMON_ERRORS.
5136
+ * For fail result we return one of SwapProvider.CREATION_FAIL_REASONS or SwapProvider.COMMON_ERRORS.*
4972
5137
  *
4973
5138
  * @param fromCoin {Coin}
4974
5139
  * @param toCoin {Coin}
@@ -4977,6 +5142,7 @@
4977
5142
  * @param refundAddress {string}
4978
5143
  * @param rawSwapData {Object|null}
4979
5144
  * @param clientIpAddress {string}
5145
+ * @param fixed {boolean}
4980
5146
  * @param [toCurrencyExtraId=""] {string} optional extra ID
4981
5147
  * @param [refundExtraId=""] {string} optional extra ID for refund address
4982
5148
  * @return {Promise<({
@@ -4989,7 +5155,8 @@
4989
5155
  * toAmount: string,
4990
5156
  * toAddress: string,
4991
5157
  * rate: string,
4992
- * fromCurrencyExtraId: string|undefined
5158
+ * fromCurrencyExtraId: string|undefined,
5159
+ * fixed: boolean
4993
5160
  * }|{
4994
5161
  * result: false,
4995
5162
  * reason: string,
@@ -4997,7 +5164,7 @@
4997
5164
  * })>}
4998
5165
  */
4999
5166
  ;
5000
- _proto.createSwap = function createSwap(fromCoin, toCoin, amount, toAddress, refundAddress, rawSwapData, clientIpAddress, toCurrencyExtraId, refundExtraId) {
5167
+ _proto.createSwap = function createSwap(fromCoin, toCoin, amount, toAddress, refundAddress, rawSwapData, clientIpAddress, fixed, toCurrencyExtraId, refundExtraId) {
5001
5168
  try {
5002
5169
  throw new Error("Not implemented in base");
5003
5170
  } catch (e) {
@@ -5052,7 +5219,9 @@
5052
5219
  SwapProvider.NO_SWAPS_REASONS = {
5053
5220
  TOO_LOW: "tooLow",
5054
5221
  TOO_HIGH: "tooHigh",
5055
- NOT_SUPPORTED: "notSupported"
5222
+ NOT_SUPPORTED: "notSupported",
5223
+ NO_FIXED_BUT_HAVE_FLOATING: "noFixedButHaveFloating",
5224
+ NO_FLOATING_BUT_HAVE_FIXED: "noFloatingButHaveFixed"
5056
5225
  };
5057
5226
  SwapProvider.CREATION_FAIL_REASONS = {
5058
5227
  RETRIABLE_FAIL: "retriableFail"
@@ -5264,7 +5433,6 @@
5264
5433
  var ticker = "" + code + (code === network ? "" : network);
5265
5434
  var defaultDecimalPlacesForCoinNotSupportedOOB = 8;
5266
5435
  var defaultMinConfirmationsForCoinNotSupportedOOB = 1;
5267
- // TODO: [dev] maybe we should recognize standard protocols?
5268
5436
  coin = new Coin(item.name, ticker, code, defaultDecimalPlacesForCoinNotSupportedOOB, null, "", null, null, defaultMinConfirmationsForCoinNotSupportedOOB, null, [], 60000, null,
5269
5437
  // We cannot recognize blockchain from swapspace data
5270
5438
  code !== network ? new Protocol(network) : null, item.contractAddress || null, false);
@@ -5277,7 +5445,7 @@
5277
5445
  network: item.network,
5278
5446
  hasExtraId: item.hasExtraId,
5279
5447
  extraIdName: item.extraIdName,
5280
- isPopular: !!(item != null && item.popular),
5448
+ isPopular: !!item.popular,
5281
5449
  iconURL: item.icon ? "https://storage.swapspace.co" + item.icon : FALLBACK_ICON_URL,
5282
5450
  deposit: (_item$deposit = item.deposit) != null ? _item$deposit : false,
5283
5451
  withdrawal: (_item$withdrawal = item.withdrawal) != null ? _item$withdrawal : false,
@@ -5335,7 +5503,7 @@
5335
5503
  };
5336
5504
  }
5337
5505
  Logger.log("Loading USDT->coin rate as not found in cache:", coin == null ? void 0 : coin.ticker);
5338
- return Promise.resolve(_this7.getSwapInfo(usdtTrc20, coin, "5000")).then(function (result) {
5506
+ return Promise.resolve(_this7.getSwapInfo(usdtTrc20, coin, "5000", false)).then(function (result) {
5339
5507
  if (!result.result) {
5340
5508
  return {
5341
5509
  result: false
@@ -5343,7 +5511,7 @@
5343
5511
  }
5344
5512
 
5345
5513
  // This calculation is not precise as we cannot recognize the actual fee and network fee. Just approximate.
5346
- var standardSwapspaceFeeMultiplier = 1.004; // usually 0.2%
5514
+ var standardSwapspaceFeeMultiplier = 1.004; // fee is usually 0.4%
5347
5515
  var rate = bignumber_js.BigNumber(1).div(bignumber_js.BigNumber(result.rate).times(standardSwapspaceFeeMultiplier)).toString();
5348
5516
  _this7._cache.put("swapspace_usdt_rate_" + coin.ticker, rate, 15 * 60000 // 15 minutes
5349
5517
  );
@@ -5371,7 +5539,10 @@
5371
5539
  improveAndRethrow(e, "getCoinByTickerIfPresent");
5372
5540
  }
5373
5541
  };
5374
- _proto.getSwapInfo = function getSwapInfo(fromCoin, toCoin, amountCoins, fromCoinToUsdRate) {
5542
+ _proto.getSwapInfo = function getSwapInfo(fromCoin, toCoin, amountCoins, fixed, fromCoinToUsdRate) {
5543
+ if (fixed === void 0) {
5544
+ fixed = false;
5545
+ }
5375
5546
  if (fromCoinToUsdRate === void 0) {
5376
5547
  fromCoinToUsdRate = null;
5377
5548
  }
@@ -5379,8 +5550,8 @@
5379
5550
  var _this8 = this;
5380
5551
  var loggerSource = "getSwapInfo";
5381
5552
  return Promise.resolve(_catch$2(function () {
5382
- if (!(fromCoin instanceof Coin) || !(toCoin instanceof Coin) || typeof amountCoins !== "string" || bignumber_js.BigNumber(amountCoins).lt("0")) {
5383
- throw new Error("Wrong input params: " + amountCoins + " " + fromCoin.ticker + " -> " + toCoin.ticker + (fromCoin instanceof Coin) + (toCoin instanceof Coin));
5553
+ if (!(fromCoin instanceof Coin) || !(toCoin instanceof Coin) || typeof amountCoins !== "string" || bignumber_js.BigNumber(amountCoins).lt("0") || fixed !== null && typeof fixed !== "boolean") {
5554
+ throw new Error("Wrong input params: " + amountCoins + " " + fromCoin.ticker + " -> " + toCoin.ticker + ", " + (fromCoin instanceof Coin) + ", " + (toCoin instanceof Coin) + ", " + typeof fixed + " " + fixed);
5384
5555
  }
5385
5556
  var fromCoinSwapspaceDetails = _this8._supportedCoins.find(function (i) {
5386
5557
  var _i$coin;
@@ -5396,17 +5567,29 @@
5396
5567
  return !fromCoinSwapspaceDetails.deposit || !toCoinSwapspaceDetails.withdrawal ? {
5397
5568
  result: false,
5398
5569
  reason: SwapProvider.NO_SWAPS_REASONS.NOT_SUPPORTED
5399
- } : Promise.resolve(axios__default["default"].get(_this8._URL + "/api/v2/amounts?fromCurrency=" + fromCoinSwapspaceDetails.code + "&fromNetwork=" + fromCoinSwapspaceDetails.network + "&toNetwork=" + toCoinSwapspaceDetails.network + "&toCurrency=" + toCoinSwapspaceDetails.code + "&amount=" + amountCoins + "&float=true&estimated=false")).then(function (response) {
5400
- var _response$data;
5570
+ } : Promise.resolve(axios__default["default"].get(_this8._URL + "/api/v2/amounts?fromCurrency=" + fromCoinSwapspaceDetails.code + "&fromNetwork=" + fromCoinSwapspaceDetails.network + "&toNetwork=" + toCoinSwapspaceDetails.network + "&toCurrency=" + toCoinSwapspaceDetails.code + "&amount=" + amountCoins + "&estimated=false")).then(function (response) {
5571
+ var _response$data, _exchangesSupportingT;
5401
5572
  Logger.log("Retrieved " + (response == null || (_response$data = response.data) == null ? void 0 : _response$data.length) + " options", loggerSource);
5402
5573
  var options = Array.isArray(response.data) ? response.data : [];
5403
- var exchangesSupportingThePair = options.filter(function (exchange) {
5574
+ var exchangesSupportingThePairDespiteFixedOrFloating = options.filter(function (exchange) {
5404
5575
  return (exchange == null ? void 0 : exchange.exists) && !BANNED_PARTNERS.find(function (bannedPartner) {
5405
5576
  return bannedPartner === (exchange == null ? void 0 : exchange.partner);
5406
- }) && (exchange == null ? void 0 : exchange.fixed) === false && (exchange.min === 0 || exchange.max === 0 || exchange.max > exchange.min || (typeof exchange.min !== "number" || typeof exchange.max !== "number") && exchange.toAmount > 0);
5577
+ }) && ((exchange == null ? void 0 : exchange.fixed) === false || (exchange == null ? void 0 : exchange.fixed) === true) && (exchange.min === 0 || exchange.max === 0 || exchange.max > exchange.min || (typeof exchange.min !== "number" || typeof exchange.max !== "number") && exchange.toAmount > 0);
5407
5578
  });
5408
- Logger.log((exchangesSupportingThePair == null ? void 0 : exchangesSupportingThePair.length) + " of them have exist=true", loggerSource);
5409
- if (!exchangesSupportingThePair.length) {
5579
+ var exchangesSupportingThePair = exchangesSupportingThePairDespiteFixedOrFloating;
5580
+ if (fixed != null) {
5581
+ exchangesSupportingThePair = exchangesSupportingThePairDespiteFixedOrFloating.filter(function (option) {
5582
+ return option.fixed === fixed;
5583
+ });
5584
+ }
5585
+ Logger.log(((_exchangesSupportingT = exchangesSupportingThePair) == null ? void 0 : _exchangesSupportingT.length) + " of them have exist=true", loggerSource);
5586
+ if (exchangesSupportingThePair.length === 0) {
5587
+ if (exchangesSupportingThePairDespiteFixedOrFloating.length > 0 && fixed !== null) {
5588
+ return {
5589
+ result: false,
5590
+ reason: fixed ? SwapProvider.NO_SWAPS_REASONS.NO_FIXED_BUT_HAVE_FLOATING : SwapProvider.NO_SWAPS_REASONS.NO_FLOATING_BUT_HAVE_FIXED
5591
+ };
5592
+ }
5410
5593
  return {
5411
5594
  result: false,
5412
5595
  reason: SwapProvider.NO_SWAPS_REASONS.NOT_SUPPORTED
@@ -5477,6 +5660,7 @@
5477
5660
  greatestMax: greatestMax,
5478
5661
  rate: rate != null ? AmountUtils.trim(rate, _this8._maxRateDigits) : null,
5479
5662
  durationMinutesRange: (_bestOpt$duration = bestOpt.duration) != null ? _bestOpt$duration : null,
5663
+ fixed: bestOpt.fixed,
5480
5664
  rawSwapData: bestOpt
5481
5665
  };
5482
5666
  }
@@ -5504,7 +5688,7 @@
5504
5688
  return Promise.reject(e);
5505
5689
  }
5506
5690
  };
5507
- _proto.createSwap = function createSwap(fromCoin, toCoin, amount, toAddress, refundAddress, rawSwapData, clientIpAddress, toCurrencyExtraId, refundExtraId) {
5691
+ _proto.createSwap = function createSwap(fromCoin, toCoin, amount, toAddress, refundAddress, rawSwapData, clientIpAddress, fixed, toCurrencyExtraId, refundExtraId) {
5508
5692
  if (toCurrencyExtraId === void 0) {
5509
5693
  toCurrencyExtraId = "";
5510
5694
  }
@@ -5516,13 +5700,18 @@
5516
5700
  var loggerSource = "createSwap";
5517
5701
  var partner = rawSwapData == null ? void 0 : rawSwapData.partner;
5518
5702
  return Promise.resolve(_catch$2(function () {
5519
- if (!(fromCoin instanceof Coin) || !(toCoin instanceof Coin) || typeof amount !== "string" || typeof toAddress !== "string" || typeof refundAddress !== "string") {
5520
- throw new Error("Invalid input: " + fromCoin + " " + toCoin + " " + amount + " " + toAddress + " " + refundAddress);
5703
+ if (!(fromCoin instanceof Coin) || !(toCoin instanceof Coin) || typeof amount !== "string" || typeof toAddress !== "string" || typeof refundAddress !== "string" || typeof clientIpAddress != "string" || typeof fixed != "boolean" || clientIpAddress.length === 0) {
5704
+ throw new Error("Invalid input: " + fromCoin + " " + toCoin + " " + amount + " " + toAddress + " " + refundAddress + " " + (clientIpAddress == null ? void 0 : clientIpAddress.length) + " " + fixed);
5521
5705
  }
5522
5706
  if (typeof partner !== "string" || typeof (rawSwapData == null ? void 0 : rawSwapData.fromCurrency) !== "string" || typeof (rawSwapData == null ? void 0 : rawSwapData.fromNetwork) !== "string" || typeof (rawSwapData == null ? void 0 : rawSwapData.toCurrency) !== "string" || typeof (rawSwapData == null ? void 0 : rawSwapData.toNetwork) !== "string" || typeof (rawSwapData == null ? void 0 : rawSwapData.id) !== "string" // can be just empty
5523
5707
  ) {
5524
5708
  throw new Error("Invalid raw swap data: " + safeStringify(rawSwapData));
5525
5709
  }
5710
+ var _this9$_supportedCoin = _this9._supportedCoins.reduce(function (prev, coinData) {
5711
+ return [coinData.coin.ticker === fromCoin.ticker ? coinData.hasExtraId : prev[0], coinData.coin.ticker === toCoin.ticker ? coinData.hasExtraId : prev[1]];
5712
+ }, [false, false]),
5713
+ fromCurrencyHasExtraId = _this9$_supportedCoin[0],
5714
+ toCurrencyHasExtraId = _this9$_supportedCoin[1];
5526
5715
  return Promise.resolve(_this9._fetchSupportedCurrenciesIfNeeded()).then(function () {
5527
5716
  var _toCurrencyExtraId, _refundExtraId;
5528
5717
  var requestData = {
@@ -5533,10 +5722,10 @@
5533
5722
  toNetwork: rawSwapData == null ? void 0 : rawSwapData.toNetwork,
5534
5723
  address: toAddress,
5535
5724
  amount: amount,
5536
- fixed: false,
5537
- extraId: (_toCurrencyExtraId = toCurrencyExtraId) != null ? _toCurrencyExtraId : "",
5538
- refundExtraId: (_refundExtraId = refundExtraId) != null ? _refundExtraId : "",
5725
+ fixed: fixed,
5726
+ extraId: toCurrencyHasExtraId ? (_toCurrencyExtraId = toCurrencyExtraId) != null ? _toCurrencyExtraId : "" : "",
5539
5727
  // This param is not documented. But the refund is usually manual so this is not critical.
5728
+ refundExtraId: fromCurrencyHasExtraId ? (_refundExtraId = refundExtraId) != null ? _refundExtraId : "" : "",
5540
5729
  rateId: rawSwapData == null ? void 0 : rawSwapData.id,
5541
5730
  userIp: clientIpAddress,
5542
5731
  refund: refundAddress
@@ -5547,7 +5736,7 @@
5547
5736
  Logger.log("Creation result " + safeStringify(result), loggerSource);
5548
5737
  if (result != null && result.id) {
5549
5738
  var _result$from, _result$from2, _result$to, _result$to2, _result$from4, _result$from5, _result$to4, _result$to5, _result$from$extraId, _result$from6;
5550
- if (typeof (result == null || (_result$from = result.from) == null ? void 0 : _result$from.amount) !== "number" || typeof (result == null || (_result$from2 = result.from) == null ? void 0 : _result$from2.address) !== "string" || typeof (result == null || (_result$to = result.to) == null ? void 0 : _result$to.amount) !== "number" || typeof (result == null || (_result$to2 = result.to) == null ? void 0 : _result$to2.address) !== "string") throw new Error("Wrong swap creation result " + result);
5739
+ if (typeof (result == null || (_result$from = result.from) == null ? void 0 : _result$from.amount) !== "number" || typeof (result == null || (_result$from2 = result.from) == null ? void 0 : _result$from2.address) !== "string" || typeof (result == null ? void 0 : result.fixed) !== "boolean" || typeof (result == null || (_result$to = result.to) == null ? void 0 : _result$to.amount) !== "number" || typeof (result == null || (_result$to2 = result.to) == null ? void 0 : _result$to2.address) !== "string") throw new Error("Wrong swap creation result " + result);
5551
5740
  /* We use the returned rate preferably but if the retrieved
5552
5741
  * rate 0/null/undefined we calculate it manually */
5553
5742
  var rate = result.rate;
@@ -5567,7 +5756,8 @@
5567
5756
  toAmount: AmountUtils.trim(result == null || (_result$to4 = result.to) == null ? void 0 : _result$to4.amount, toCoin.digits),
5568
5757
  toAddress: result == null || (_result$to5 = result.to) == null ? void 0 : _result$to5.address,
5569
5758
  fromCurrencyExtraId: (_result$from$extraId = result == null || (_result$from6 = result.from) == null ? void 0 : _result$from6.extraId) != null ? _result$from$extraId : "",
5570
- rate: AmountUtils.trim(rate, _this9._maxRateDigits)
5759
+ rate: AmountUtils.trim(rate, _this9._maxRateDigits),
5760
+ fixed: result.fixed
5571
5761
  };
5572
5762
  }
5573
5763
  var errorMessage = "Swap creation succeeded but the response is wrong: " + safeStringify(response);
@@ -5684,7 +5874,7 @@
5684
5874
  var status = _this10._mapSwapspaceStatusToRabbitStatus(swap.status, isExpiredByTime);
5685
5875
  var toDigits = status === SwapProvider.SWAP_STATUSES.REFUNDED ? fromCoin.digits : toCoin.digits;
5686
5876
  var addressToSendCoinsToSwapspace = swap.from.address;
5687
- return new ExistingSwap(swapIds[index], status, toUtcTimestamp(swap.timestamps.createdAt), expiresAt, swap.confirmations, AmountUtils.trim(swap.rate, _this10._maxRateDigits), swap.refundAddress, addressToSendCoinsToSwapspace, fromCoin, AmountUtils.trim(swap.from.amount, fromCoin.digits), swap.from.transactionHash, swap.blockExplorerTransactionUrl.from, toCoin, AmountUtils.trim(swap.to.amount, toDigits), swap.to.transactionHash, swap.blockExplorerTransactionUrl.to, swap.to.address, swap.partner, (_swap$from$extraId = swap.from.extraId) != null ? _swap$from$extraId : null, (_swap$to$extraId = swap.to.extraId) != null ? _swap$to$extraId : null, (_swap$refundExtraId = swap.refundExtraId) != null ? _swap$refundExtraId : null);
5877
+ return new ExistingSwap(swapIds[index], status, toUtcTimestamp(swap.timestamps.createdAt), expiresAt, swap.confirmations, AmountUtils.trim(swap.rate, _this10._maxRateDigits), swap.fixed, swap.refundAddress, addressToSendCoinsToSwapspace, fromCoin, AmountUtils.trim(swap.from.amount, fromCoin.digits), swap.from.transactionHash, swap.blockExplorerTransactionUrl.from, toCoin, AmountUtils.trim(swap.to.amount, toDigits), swap.to.transactionHash, swap.blockExplorerTransactionUrl.to, swap.to.address, swap.partner, (_swap$from$extraId = swap.from.extraId) != null ? _swap$from$extraId : null, (_swap$to$extraId = swap.to.extraId) != null ? _swap$to$extraId : null, (_swap$refundExtraId = swap.refundExtraId) != null ? _swap$refundExtraId : null);
5688
5878
  }).flat();
5689
5879
  Logger.log("Swap details result " + safeStringify(swaps), loggerSource);
5690
5880
  return {
@@ -6242,6 +6432,7 @@
6242
6432
  * @param fromCoin {Coin}
6243
6433
  * @param toCoin {Coin}
6244
6434
  * @param fromAmountCoins {string}
6435
+ * @param [fixed=false] {boolean|null} null means fixed or float doesn't matter
6245
6436
  * @param [withoutFiat=false] {boolean} pass true if you don't need the fiat equivalent - this will diminish requests count
6246
6437
  * @return {Promise<{
6247
6438
  * result: false,
@@ -6257,7 +6448,10 @@
6257
6448
  * }>}
6258
6449
  */
6259
6450
  ;
6260
- _proto.getPublicSwapDetails = function getPublicSwapDetails(fromCoin, toCoin, fromAmountCoins, withoutFiat) {
6451
+ _proto.getPublicSwapDetails = function getPublicSwapDetails(fromCoin, toCoin, fromAmountCoins, fixed, withoutFiat) {
6452
+ if (fixed === void 0) {
6453
+ fixed = false;
6454
+ }
6261
6455
  if (withoutFiat === void 0) {
6262
6456
  withoutFiat = false;
6263
6457
  }
@@ -6268,7 +6462,7 @@
6268
6462
  function _temp2(_this7$_swapProvider$) {
6269
6463
  var _this7$_swapProvider$2;
6270
6464
  var coinUsdtRate = withoutFiat ? _this7$_swapProvider$ : (_this7$_swapProvider$2 = _this7$_swapProvider$ == null ? void 0 : _this7$_swapProvider$.rate) != null ? _this7$_swapProvider$2 : null;
6271
- return Promise.resolve(_this7._swapProvider.getSwapInfo(fromCoin, toCoin, fromAmountCoins, coinUsdtRate)).then(function (details) {
6465
+ return Promise.resolve(_this7._swapProvider.getSwapInfo(fromCoin, toCoin, fromAmountCoins, fixed, coinUsdtRate)).then(function (details) {
6272
6466
  var _result$swapCreationI, _result$swapCreationI2;
6273
6467
  var min = details.result ? details.min : details.smallestMin;
6274
6468
  var max = details.result ? details.max : details.greatestMax;
@@ -6295,9 +6489,15 @@
6295
6489
  };
6296
6490
  };
6297
6491
  if (!details.result) {
6298
- if ((details == null ? void 0 : details.reason) === SwapProvider.NO_SWAPS_REASONS.NOT_SUPPORTED) return composeFailResult(PublicSwapService.PUBLIC_SWAP_DETAILS_FAIL_REASONS.PAIR_NOT_SUPPORTED);else if ((details == null ? void 0 : details.reason) === SwapProvider.COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED) {
6492
+ if ((details == null ? void 0 : details.reason) === SwapProvider.NO_SWAPS_REASONS.NOT_SUPPORTED) {
6493
+ return composeFailResult(PublicSwapService.PUBLIC_SWAP_DETAILS_FAIL_REASONS.PAIR_NOT_SUPPORTED);
6494
+ } else if ((details == null ? void 0 : details.reason) === SwapProvider.COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED) {
6299
6495
  SwapUtils.safeHandleRequestsLimitExceeding();
6300
6496
  return composeFailResult(PublicSwapService.PUBLIC_SWAPS_COMMON_ERRORS.REQUESTS_LIMIT_EXCEEDED);
6497
+ } else if ((details == null ? void 0 : details.reason) === SwapProvider.NO_SWAPS_REASONS.NO_FLOATING_BUT_HAVE_FIXED) {
6498
+ return composeFailResult(PublicSwapService.PUBLIC_SWAP_DETAILS_FAIL_REASONS.NO_FLOATING_BUT_HAVE_FIXED_PUBLIC_SWAP_OPTION);
6499
+ } else if ((details == null ? void 0 : details.reason) === SwapProvider.NO_SWAPS_REASONS.NO_FIXED_BUT_HAVE_FLOATING) {
6500
+ return composeFailResult(PublicSwapService.PUBLIC_SWAP_DETAILS_FAIL_REASONS.NO_FIXED_BUT_HAVE_FLOATING_PUBLIC_SWAP_OPTION);
6301
6501
  }
6302
6502
  }
6303
6503
  var fromAmountBigNumber = bignumber_js.BigNumber(fromAmountCoins);
@@ -6309,7 +6509,7 @@
6309
6509
  var toAmountCoins = AmountUtils.trim(fromAmountBigNumber.times(details.rate), fromCoin.digits);
6310
6510
  var result = {
6311
6511
  result: true,
6312
- swapCreationInfo: new BaseSwapCreationInfo(fromCoin, toCoin, fromAmountCoins, toAmountCoins, details.rate, details.rawSwapData, min, fiatMin, max, fiatMax, details.durationMinutesRange)
6512
+ swapCreationInfo: new BaseSwapCreationInfo(fromCoin, toCoin, fromAmountCoins, toAmountCoins, details.rate, details.rawSwapData, min, fiatMin, max, fiatMax, details.durationMinutesRange, details.fixed)
6313
6513
  };
6314
6514
  Logger.log("Result: " + safeStringify({
6315
6515
  result: result.result,
@@ -6355,7 +6555,8 @@
6355
6555
  * fromCoin: Coin,
6356
6556
  * rate: string,
6357
6557
  * swapId: string,
6358
- * fromCurrencyExtraId: string
6558
+ * fromCurrencyExtraId: string,
6559
+ * fixed: boolean
6359
6560
  * }|{
6360
6561
  * result: false,
6361
6562
  * reason: string
@@ -6375,7 +6576,7 @@
6375
6576
  fromCoin: swapCreationInfo == null || (_swapCreationInfo$fro = swapCreationInfo.fromCoin) == null ? void 0 : _swapCreationInfo$fro.ticker,
6376
6577
  toCoin: swapCreationInfo == null || (_swapCreationInfo$toC = swapCreationInfo.toCoin) == null ? void 0 : _swapCreationInfo$toC.ticker
6377
6578
  })), loggerSource);
6378
- return Promise.resolve(_this8._swapProvider.createSwap(fromCoin, toCoin, fromAmount, toAddress, refundAddress, swapCreationInfo.rawSwapData, clientIp, toCurrencyExtraId, refundExtraId)).then(function (result) {
6579
+ return Promise.resolve(_this8._swapProvider.createSwap(fromCoin, toCoin, fromAmount, toAddress, refundAddress, swapCreationInfo.rawSwapData, clientIp, swapCreationInfo.fixed, toCurrencyExtraId, refundExtraId)).then(function (result) {
6379
6580
  var _exit;
6380
6581
  function _temp6(_result8) {
6381
6582
  if (_exit) return _result8;
@@ -6405,6 +6606,7 @@
6405
6606
  if (result.result && result != null && result.swapId) {
6406
6607
  var _temp4 = function _temp4() {
6407
6608
  var _result$fromCurrencyE;
6609
+ // TODO: feature, cirtical] add GA event. task_id=tbd
6408
6610
  EventBusInstance__default["default"].dispatch(PublicSwapService.PUBLIC_SWAP_CREATED_EVENT, null, fromCoin.ticker, toCoin.ticker, _fromAmountFiat);
6409
6611
  var toReturn = {
6410
6612
  result: true,
@@ -6421,7 +6623,9 @@
6421
6623
  durationMinutesRange: swapCreationInfo.durationMinutesRange,
6422
6624
  address: result.fromAddress,
6423
6625
  // CRITICAL: this is the address to send coins to swaps provider
6424
- fromCurrencyExtraId: (_result$fromCurrencyE = result.fromCurrencyExtraId) != null ? _result$fromCurrencyE : "" // CRITICAL: this is the extra ID for address to send coins to swaps provider
6626
+ fromCurrencyExtraId: (_result$fromCurrencyE = result.fromCurrencyExtraId) != null ? _result$fromCurrencyE : "",
6627
+ // CRITICAL: this is the extra ID for address to send coins to swaps provider
6628
+ fixed: result.fixed
6425
6629
  };
6426
6630
  _this8._savePublicSwapIdLocally(result.swapId);
6427
6631
  Logger.log("Returning: " + safeStringify(_extends({}, toReturn, {
@@ -6682,7 +6886,9 @@
6682
6886
  PublicSwapService.PUBLIC_SWAP_DETAILS_FAIL_REASONS = {
6683
6887
  AMOUNT_LESS_THAN_MIN_SWAPPABLE: "amountLessThanMinSwappable",
6684
6888
  AMOUNT_HIGHER_THAN_MAX_SWAPPABLE: "amountHigherThanMaxSwappable",
6685
- PAIR_NOT_SUPPORTED: "pairNotSupported"
6889
+ PAIR_NOT_SUPPORTED: "pairNotSupported",
6890
+ NO_FIXED_BUT_HAVE_FLOATING_PUBLIC_SWAP_OPTION: "noFixedButHaveFloatingPublicSwapOption",
6891
+ NO_FLOATING_BUT_HAVE_FIXED_PUBLIC_SWAP_OPTION: "noFloatingButHaveFixedPublicSwapOption"
6686
6892
  };
6687
6893
  PublicSwapService._fiatDecimalsCount = FiatCurrenciesService.getCurrencyDecimalCountByCode("USD");
6688
6894
 
@@ -6705,6 +6911,8 @@
6705
6911
  exports.ExistingSwapWithFiatData = ExistingSwapWithFiatData;
6706
6912
  exports.ExternalApiProvider = ExternalApiProvider;
6707
6913
  exports.FiatCurrenciesService = FiatCurrenciesService;
6914
+ exports.InputValuesProviders = InputValuesProviders;
6915
+ exports.IpAddressProvider = IpAddressProvider;
6708
6916
  exports.LoadingDots = LoadingDots;
6709
6917
  exports.Logger = Logger;
6710
6918
  exports.LogsStorage = LogsStorage;