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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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");
@@ -2682,7 +2682,7 @@
2682
2682
  return Cache;
2683
2683
  }();
2684
2684
 
2685
- function _catch$7(body, recover) {
2685
+ function _catch$8(body, recover) {
2686
2686
  try {
2687
2687
  var result = body();
2688
2688
  } catch (e) {
@@ -2700,7 +2700,7 @@
2700
2700
  return new Promise(function (resolve, reject) {
2701
2701
  setTimeout(function () {
2702
2702
  try {
2703
- var _temp = _catch$7(function () {
2703
+ var _temp = _catch$8(function () {
2704
2704
  return Promise.resolve(execution()).then(function (_execution) {
2705
2705
  resolve(_execution);
2706
2706
  });
@@ -2784,7 +2784,7 @@
2784
2784
  return AxiosAdapter;
2785
2785
  }();
2786
2786
 
2787
- function _catch$6(body, recover) {
2787
+ function _catch$7(body, recover) {
2788
2788
  try {
2789
2789
  var result = body();
2790
2790
  } catch (e) {
@@ -2800,7 +2800,7 @@
2800
2800
  EmailsApi.sendEmail = function sendEmail(subject, body) {
2801
2801
  try {
2802
2802
  var _this = this;
2803
- var _temp = _catch$6(function () {
2803
+ var _temp = _catch$7(function () {
2804
2804
  var url = window.location.protocol + "//" + window.location.host + "/api/v1/" + _this.serverEndpointEntity;
2805
2805
  return Promise.resolve(axios__default["default"].post(url, {
2806
2806
  subject: subject,
@@ -2818,772 +2818,317 @@
2818
2818
  }();
2819
2819
  EmailsApi.serverEndpointEntity = "emails";
2820
2820
 
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;
2821
+ var ExternalApiProvider = /*#__PURE__*/function () {
2822
+ /**
2823
+ * Creates an instance of external api provider.
2824
+ *
2825
+ * If you need sub-request then use 'subRequestIndex' to check current request index in functions below.
2826
+ * Also use array for 'httpMethod'.
2827
+ *
2828
+ * If the endpoint of dedicated provider has pagination then you should customize the behavior using
2829
+ * "changeQueryParametersForPageNumber", "checkWhetherResponseIsForLastPage".
2830
+ *
2831
+ * We perform RPS counting all over the App to avoid blocking our clients due to abuses of the providers.
2832
+ *
2833
+ * @param endpoint {string} URL to the provider's endpoint. Note: you can customize it using composeQueryString
2834
+ * @param [httpMethod] {string|string[]} one of "get", "post", "put", "patch", "delete" or an array of these values
2835
+ * for request having sub-requests
2836
+ * @param [timeout] {number} number of milliseconds to wait for the response
2837
+ * @param [apiGroup] {ApiGroup} singleton object containing parameters of API group. Helpful when you use the same
2838
+ * api for different providers to avoid hardcoding RPS inside each provider what can cause mistakes
2839
+ * @param [specificHeaders] {Object} contains specific keys (headers) and values (their content) if needed for this provider
2840
+ * @param [maxPageLength] {number} optional number of items per page if the request supports pagination
2841
+ */
2842
+ function ExternalApiProvider(endpoint, httpMethod, timeout, apiGroup, specificHeaders, maxPageLength) {
2843
+ var _maxPageLength, _specificHeaders;
2844
+ if (specificHeaders === void 0) {
2845
+ specificHeaders = {};
2864
2846
  }
2865
- pact.s = state;
2866
- pact.v = value;
2867
- const observer = pact.o;
2868
- if (observer) {
2869
- observer(pact);
2847
+ if (maxPageLength === void 0) {
2848
+ maxPageLength = Number.MAX_SAFE_INTEGER;
2870
2849
  }
2850
+ this.endpoint = endpoint;
2851
+ this.httpMethod = httpMethod != null ? httpMethod : "get";
2852
+ // TODO: [refactoring, critical] We have two timeouts for robust data retrieval - here and inside the service method call, need to remain the only
2853
+ this.timeout = timeout != null ? timeout : 10000;
2854
+ // TODO: [refactoring, critical] We need single place for all RPSes as we use them as hardcoded constants now inside different services
2855
+ this.apiGroup = apiGroup;
2856
+ this.maxPageLength = (_maxPageLength = maxPageLength) != null ? _maxPageLength : Number.MAX_SAFE_INTEGER;
2857
+ this.niceFactor = 1;
2858
+ this.specificHeaders = (_specificHeaders = specificHeaders) != null ? _specificHeaders : {};
2871
2859
  }
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;
2860
+ var _proto = ExternalApiProvider.prototype;
2861
+ _proto.getRps = function getRps() {
2862
+ var _this$apiGroup$rps;
2863
+ return (_this$apiGroup$rps = this.apiGroup.rps) != null ? _this$apiGroup$rps : 2;
2911
2864
  };
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
- }
2865
+ _proto.isRpsExceeded = function isRpsExceeded() {
2866
+ return this.apiGroup.isRpsExceeded();
2867
+ };
2868
+ _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
2869
+ this.apiGroup.actualizeLastCalledTimestamp();
2870
+ };
2871
+ _proto.getApiGroupId = function getApiGroupId() {
2872
+ return this.apiGroup.id;
2947
2873
  }
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);
2874
+
2875
+ /**
2876
+ * Some endpoint can require several sub requests. Example is one request to get confirmed transactions
2877
+ * and another request for unconfirmed transactions. You should override this method to return true for such requests.
2878
+ *
2879
+ * @return {boolean} true if this provider requires several requests to retrieve the data
2880
+ */;
2881
+ _proto.doesRequireSubRequests = function doesRequireSubRequests() {
2882
+ return false;
2977
2883
  }
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
- }
2884
+
2885
+ /**
2886
+ * Some endpoint support pagination. Override this method if so and implement corresponding methods.
2887
+ *
2888
+ * @return {boolean} true if this provider requires several requests to retrieve the data
2889
+ */;
2890
+ _proto.doesSupportPagination = function doesSupportPagination() {
2891
+ return false;
2989
2892
  }
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);
2893
+
2894
+ /**
2895
+ * Composes a query string to be added to the endpoint of this provider.
2896
+ *
2897
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2898
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2899
+ * @returns {string} query string to be concatenated with endpoint
2900
+ */;
2901
+ _proto.composeQueryString = function composeQueryString(params, subRequestIndex) {
2902
+ return "";
3053
2903
  }
3054
2904
 
3055
2905
  /**
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.
2906
+ * Composes a body to be added to the request
3066
2907
  *
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
- }
2908
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2909
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2910
+ * @returns {string}
2911
+ */;
2912
+ _proto.composeBody = function composeBody(params, subRequestIndex) {
2913
+ return "";
3130
2914
  }
2915
+
3131
2916
  /**
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.
2917
+ * Extracts data from the response and returns it
3134
2918
  *
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;
2919
+ * @param response {Object} HTTP response returned by provider
2920
+ * @param [params] {any[]} params array passed to the RobustExternalAPICallerService
2921
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2922
+ * @param iterationsData {any[]} array of data retrieved from previous sub-requests
2923
+ * @returns {any}
2924
+ */;
2925
+ _proto.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
2926
+ return [];
3148
2927
  }
3149
2928
 
3150
2929
  /**
3151
- * Directly acquires the lock despite on cached data availability.
3152
- * So if this method returns result === true you can start the data retrieval.
2930
+ * Function changing the query string according to page number and previous response
2931
+ * Only for endpoints supporting pagination
3153
2932
  *
3154
- * @param cacheId {string}
3155
- * @return {Promise<{ result: true, lockId: string }|{ result: false }>}
2933
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
2934
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
2935
+ * @param pageNumber {number} new page number. We count from 0. You need to manually increment with 1 if your
2936
+ * provider counts pages starting with 1
2937
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2938
+ * @returns {any[]}
3156
2939
  */;
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
- }
2940
+ _proto.changeQueryParametersForPageNumber = function changeQueryParametersForPageNumber(params, previousResponse, pageNumber, subRequestIndex) {
2941
+ return params;
3168
2942
  }
2943
+
3169
2944
  /**
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.
2945
+ * Function checking whether the response is for the last page to stop requesting for a next page.
2946
+ * Only for endpoints supporting pagination.
3177
2947
  *
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
- }
2948
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
2949
+ * @param currentResponse {Object} HTTP response returned by provider for current call (current page, next after the previous)
2950
+ * @param currentPageNumber {number} current page number (for current response)
2951
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
2952
+ * @returns {boolean}
2953
+ */;
2954
+ _proto.checkWhetherResponseIsForLastPage = function checkWhetherResponseIsForLastPage(previousResponse, currentResponse, currentPageNumber, subRequestIndex) {
2955
+ return true;
3215
2956
  }
3216
2957
 
3217
2958
  /**
3218
- * Should be called then and only then if you successfully acquired a lock with the lock id.
3219
- *
3220
- * @param cacheId {string}
3221
- * @param lockId {string}
2959
+ * Resets the nice factor to default value
3222
2960
  */;
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
- }
2961
+ _proto.resetNiceFactor = function resetNiceFactor() {
2962
+ this.niceFactor = 1;
3231
2963
  }
3232
2964
 
3233
2965
  /**
3234
- * Actualized currently present cached data by key. Applies the provided function to the cached data.
2966
+ * Internal method used for requests requiring sub-requests.
3235
2967
  *
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
2968
+ * @param iterationsData {any[]} iterations data retrieved from getDataByResponse called per sub-request.
2969
+ * @return {any} by default flatten the passed iterations data array. Should be redefined if you need another logic.
3245
2970
  */;
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
- }
2971
+ _proto.incorporateIterationsData = function incorporateIterationsData(iterationsData) {
2972
+ return iterationsData.flat();
2973
+ };
2974
+ return ExternalApiProvider;
2975
+ }();
3259
2976
 
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");
2977
+ /**
2978
+ * Models a group of APIs provided by the same owner and used for different services in our app.
2979
+ * It means we need to mention RPS several times for each usage and also have some holder of last call timestamp per
2980
+ * api group. So this concept allows to use it for exact ExternalApiProvider and make sure that you use the same
2981
+ * RPS value and make decisions on base of the same timestamp of last call to the API group owner.
2982
+ */
2983
+ var ApiGroup = /*#__PURE__*/function () {
2984
+ function ApiGroup(id, rps, backendProxyIdGenerator) {
2985
+ if (backendProxyIdGenerator === void 0) {
2986
+ backendProxyIdGenerator = null;
3277
2987
  }
2988
+ this.id = id;
2989
+ this.rps = rps;
2990
+ this.lastCalledTimestamp = null;
2991
+ this.backendProxyIdGenerator = backendProxyIdGenerator;
2992
+ }
2993
+ var _proto = ApiGroup.prototype;
2994
+ _proto.isRpsExceeded = function isRpsExceeded() {
2995
+ var _this$lastCalledTimes;
2996
+ return ((_this$lastCalledTimes = this.lastCalledTimestamp) != null ? _this$lastCalledTimes : 0) + Math.floor(1000 / this.rps) > Date.now();
3278
2997
  };
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);
2998
+ _proto.actualizeLastCalledTimestamp = function actualizeLastCalledTimestamp() {
2999
+ this.lastCalledTimestamp = Date.now();
3294
3000
  };
3295
- return CacheAndConcurrentRequestsResolver;
3001
+ return ApiGroup;
3296
3002
  }();
3297
- var ManagerOfRequestsToTheSameResource = /*#__PURE__*/function () {
3003
+ var ApiGroups = {
3298
3004
  /**
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
3005
+ * Currently we use free version of etherscan provider with 0.2 RPS. But we have API key with 100k requests free
3006
+ * per month. So we can add it if not enough current RPS.
3302
3007
  */
3303
- function ManagerOfRequestsToTheSameResource(bio, maxPollsCount, timeoutDuration) {
3304
- if (maxPollsCount === void 0) {
3305
- maxPollsCount = 100;
3008
+ ETHERSCAN: new ApiGroup("etherscan", 0.17),
3009
+ // Actually 0.2 but fails sometime, so we use smaller
3010
+ ALCHEMY: new ApiGroup("alchemy", 0.3, function (networkKey) {
3011
+ return "alchemy-" + networkKey;
3012
+ }),
3013
+ BLOCKSTREAM: new ApiGroup("blockstream", 0.2),
3014
+ BLOCKCHAIN_INFO: new ApiGroup("blockchain.info", 1),
3015
+ BLOCKNATIVE: new ApiGroup("blocknative", 0.5),
3016
+ ETHGASSTATION: new ApiGroup("ethgasstation", 0.5),
3017
+ TRONGRID: new ApiGroup("trongrid", 0.3, function (networkKey) {
3018
+ return "trongrid-" + networkKey;
3019
+ }),
3020
+ TRONSCAN: new ApiGroup("tronscan", 0.3),
3021
+ GETBLOCK: new ApiGroup("getblock", 0.3),
3022
+ COINCAP: new ApiGroup("coincap", 0.5),
3023
+ // 200 per minute without API key
3024
+ COINGECKO: new ApiGroup("coingecko", 0.9),
3025
+ // actually 0.13-0.5 according to the docs but we use smaller due to expirienced frequent abuses
3026
+ MESSARI: new ApiGroup("messari", 0.2),
3027
+ BTCCOM: new ApiGroup("btccom", 0.2),
3028
+ BITAPS: new ApiGroup("bitaps", 0.25),
3029
+ // Docs say that RPS is 3 but using it causes frequent 429 HTTP errors
3030
+ CEX: new ApiGroup("cex", 0.5),
3031
+ // Just assumption for RPS
3032
+ BIGDATACLOUD: new ApiGroup("bigdatacloud", 1),
3033
+ // Just assumption for RPS
3034
+ TRACKIP: new ApiGroup("trackip", 1),
3035
+ // Just assumption for RPS
3036
+ IPIFY: new ApiGroup("ipify", 1),
3037
+ // Just assumption for RPS
3038
+ WHATISMYIPADDRESS: new ApiGroup("whatismyipaddress", 1),
3039
+ // Just assumption for RPS
3040
+ EXCHANGERATE: new ApiGroup("exchangerate", 1),
3041
+ // Just assumption for RPS
3042
+ FRANKFURTER: new ApiGroup("frankfurter", 1),
3043
+ // Just assumption for RPS
3044
+ BITGO: new ApiGroup("bitgo", 1),
3045
+ // Just assumption for RPS
3046
+ BITCOINER: new ApiGroup("bitcoiner", 1),
3047
+ // Just assumption for RPS
3048
+ BITCORE: new ApiGroup("bitcore", 1),
3049
+ // Just assumption for RPS
3050
+ // BLOCKCHAIR: new ApiGroup("blockchair", 0.04), // this provider require API key for commercial use (10usd 10000 reqs), we will add it later
3051
+ MEMPOOL: new ApiGroup("mempool", 0.2) // Just assumption for RPS
3052
+ };
3053
+
3054
+ // TODO: [refactoring, low] Consider removing this logic task_id=c360f2af75764bde8badd9ff1cc00d48
3055
+ var ConcurrentCalculationsMetadataHolder = /*#__PURE__*/function () {
3056
+ function ConcurrentCalculationsMetadataHolder() {
3057
+ this._calculations = {};
3058
+ }
3059
+ var _proto = ConcurrentCalculationsMetadataHolder.prototype;
3060
+ _proto.startCalculation = function startCalculation(domain, calculationsHistoryMaxLength) {
3061
+ if (calculationsHistoryMaxLength === void 0) {
3062
+ calculationsHistoryMaxLength = 100;
3306
3063
  }
3307
- if (timeoutDuration === void 0) {
3308
- timeoutDuration = 1000;
3064
+ if (!this._calculations[domain]) {
3065
+ this._calculations[domain] = [];
3066
+ }
3067
+ if (this._calculations[domain].length > calculationsHistoryMaxLength) {
3068
+ this._calculations[domain] = this._calculations[domain].slice(Math.round(calculationsHistoryMaxLength * 0.2));
3069
+ }
3070
+ var newCalculation = {
3071
+ startTimestamp: Date.now(),
3072
+ endTimestamp: null,
3073
+ uuid: uuid.v4()
3074
+ };
3075
+ this._calculations[domain].push(newCalculation);
3076
+ return newCalculation.uuid;
3077
+ };
3078
+ _proto.endCalculation = function endCalculation(domain, uuid, isFailed) {
3079
+ if (isFailed === void 0) {
3080
+ isFailed = false;
3309
3081
  }
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
3082
  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);
3083
+ var _calculation$endTimes, _calculation$startTim, _calculation$uuid;
3084
+ var calculation = this._calculations[domain].find(function (calculation) {
3085
+ return (calculation == null ? void 0 : calculation.uuid) === uuid;
3344
3086
  });
3345
- return Promise.resolve(_temp3 && _temp3.then ? _temp3.then(function (_result3) {
3346
- return _exit ? _result3 : null;
3347
- }) : _exit ? _temp3 : null);
3087
+ if (calculation) {
3088
+ calculation.endTimestamp = Date.now();
3089
+ calculation.isFiled = isFailed;
3090
+ }
3091
+ 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);
3092
+ Logger.log("endCalculation", elapsed + " ms: " + domain + "." + ((_calculation$uuid = calculation == null ? void 0 : calculation.uuid) != null ? _calculation$uuid : "").slice(0, 7));
3093
+ return calculation;
3348
3094
  } catch (e) {
3349
- return Promise.reject(e);
3095
+ Logger.logError(e, "endCalculation");
3096
+ }
3097
+ };
3098
+ _proto.isCalculationLate = function isCalculationLate(domain, uuid) {
3099
+ var queue = this._calculations[domain];
3100
+ var analysingCalculation = queue.find(function (item) {
3101
+ return item.uuid === uuid;
3102
+ });
3103
+ return analysingCalculation && !!queue.find(function (calculation) {
3104
+ return calculation.endTimestamp != null && calculation.startTimestamp > analysingCalculation.startTimestamp;
3105
+ });
3106
+ };
3107
+ _proto.printCalculationsWaitingMoreThanSpecifiedSeconds = function printCalculationsWaitingMoreThanSpecifiedSeconds(waitingLastsMs) {
3108
+ var _this = this;
3109
+ if (waitingLastsMs === void 0) {
3110
+ waitingLastsMs = 2000;
3350
3111
  }
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");
3551
- }
3552
- };
3553
- _proto.isCalculationLate = function isCalculationLate(domain, uuid) {
3554
- var queue = this._calculations[domain];
3555
- var analysingCalculation = queue.find(function (item) {
3556
- return item.uuid === uuid;
3557
- });
3558
- return analysingCalculation && !!queue.find(function (calculation) {
3559
- return calculation.endTimestamp != null && calculation.startTimestamp > analysingCalculation.startTimestamp;
3560
- });
3561
- };
3562
- _proto.printCalculationsWaitingMoreThanSpecifiedSeconds = function printCalculationsWaitingMoreThanSpecifiedSeconds(waitingLastsMs) {
3563
- var _this = this;
3564
- if (waitingLastsMs === void 0) {
3565
- waitingLastsMs = 2000;
3566
- }
3567
- var calculations = Object.keys(this._calculations).map(function (domain) {
3568
- return _this._calculations[domain].map(function (c) {
3569
- return _extends({}, c, {
3570
- domain: domain
3571
- });
3572
- });
3573
- }).flat().filter(function (c) {
3574
- return c.endTimestamp === null && Date.now() - c.startTimestamp > waitingLastsMs;
3575
- });
3576
- Logger.log("printCalculationsWaitingMoreThanSpecifiedSeconds", "Calculations waiting more than " + (waitingLastsMs / 1000).toFixed(1) + "s:\n" + calculations.map(function (c) {
3577
- return c.domain + "." + c.uuid.slice(0, 8) + ": " + (Date.now() - c.startTimestamp) + "\n";
3578
- }));
3579
- };
3580
- return ConcurrentCalculationsMetadataHolder;
3581
- }();
3582
- var concurrentCalculationsMetadataHolder = new ConcurrentCalculationsMetadataHolder();
3583
-
3584
- var ExternalServicesStatsCollector = /*#__PURE__*/function () {
3585
- function ExternalServicesStatsCollector() {
3586
- this.stats = new Map();
3112
+ var calculations = Object.keys(this._calculations).map(function (domain) {
3113
+ return _this._calculations[domain].map(function (c) {
3114
+ return _extends({}, c, {
3115
+ domain: domain
3116
+ });
3117
+ });
3118
+ }).flat().filter(function (c) {
3119
+ return c.endTimestamp === null && Date.now() - c.startTimestamp > waitingLastsMs;
3120
+ });
3121
+ Logger.log("printCalculationsWaitingMoreThanSpecifiedSeconds", "Calculations waiting more than " + (waitingLastsMs / 1000).toFixed(1) + "s:\n" + calculations.map(function (c) {
3122
+ return c.domain + "." + c.uuid.slice(0, 8) + ": " + (Date.now() - c.startTimestamp) + "\n";
3123
+ }));
3124
+ };
3125
+ return ConcurrentCalculationsMetadataHolder;
3126
+ }();
3127
+ var concurrentCalculationsMetadataHolder = new ConcurrentCalculationsMetadataHolder();
3128
+
3129
+ var ExternalServicesStatsCollector = /*#__PURE__*/function () {
3130
+ function ExternalServicesStatsCollector() {
3131
+ this.stats = new Map();
3587
3132
  }
3588
3133
  var _proto = ExternalServicesStatsCollector.prototype;
3589
3134
  _proto.externalServiceFailed = function externalServiceFailed(serviceUrl, message) {
@@ -3673,7 +3218,7 @@
3673
3218
  * improve the reliability of a data retrieval.
3674
3219
  */
3675
3220
 
3676
- function _catch$4(body, recover) {
3221
+ function _catch$6(body, recover) {
3677
3222
  try {
3678
3223
  var result = body();
3679
3224
  } catch (e) {
@@ -3684,21 +3229,21 @@
3684
3229
  }
3685
3230
  return result;
3686
3231
  }
3687
- function _settle$1(pact, state, value) {
3232
+ function _settle$2(pact, state, value) {
3688
3233
  if (!pact.s) {
3689
- if (value instanceof _Pact$1) {
3234
+ if (value instanceof _Pact$2) {
3690
3235
  if (value.s) {
3691
3236
  if (state & 1) {
3692
3237
  state = value.s;
3693
3238
  }
3694
3239
  value = value.v;
3695
3240
  } else {
3696
- value.o = _settle$1.bind(null, pact, state);
3241
+ value.o = _settle$2.bind(null, pact, state);
3697
3242
  return;
3698
3243
  }
3699
3244
  }
3700
3245
  if (value && value.then) {
3701
- value.then(_settle$1.bind(null, pact, state), _settle$1.bind(null, pact, 2));
3246
+ value.then(_settle$2.bind(null, pact, state), _settle$2.bind(null, pact, 2));
3702
3247
  return;
3703
3248
  }
3704
3249
  pact.s = state;
@@ -3709,7 +3254,7 @@
3709
3254
  }
3710
3255
  }
3711
3256
  }
3712
- var _Pact$1 = /*#__PURE__*/function () {
3257
+ var _Pact$2 = /*#__PURE__*/function () {
3713
3258
  function _Pact() {}
3714
3259
  _Pact.prototype.then = function (onFulfilled, onRejected) {
3715
3260
  var result = new _Pact();
@@ -3718,9 +3263,9 @@
3718
3263
  var callback = state & 1 ? onFulfilled : onRejected;
3719
3264
  if (callback) {
3720
3265
  try {
3721
- _settle$1(result, 1, callback(this.v));
3266
+ _settle$2(result, 1, callback(this.v));
3722
3267
  } catch (e) {
3723
- _settle$1(result, 2, e);
3268
+ _settle$2(result, 2, e);
3724
3269
  }
3725
3270
  return result;
3726
3271
  } else {
@@ -3731,28 +3276,28 @@
3731
3276
  try {
3732
3277
  var value = _this.v;
3733
3278
  if (_this.s & 1) {
3734
- _settle$1(result, 1, onFulfilled ? onFulfilled(value) : value);
3279
+ _settle$2(result, 1, onFulfilled ? onFulfilled(value) : value);
3735
3280
  } else if (onRejected) {
3736
- _settle$1(result, 1, onRejected(value));
3281
+ _settle$2(result, 1, onRejected(value));
3737
3282
  } else {
3738
- _settle$1(result, 2, value);
3283
+ _settle$2(result, 2, value);
3739
3284
  }
3740
3285
  } catch (e) {
3741
- _settle$1(result, 2, e);
3286
+ _settle$2(result, 2, e);
3742
3287
  }
3743
3288
  };
3744
3289
  return result;
3745
3290
  };
3746
3291
  return _Pact;
3747
3292
  }();
3748
- function _isSettledPact$1(thenable) {
3749
- return thenable instanceof _Pact$1 && thenable.s & 1;
3293
+ function _isSettledPact$2(thenable) {
3294
+ return thenable instanceof _Pact$2 && thenable.s & 1;
3750
3295
  }
3751
- function _for(test, update, body) {
3296
+ function _for$1(test, update, body) {
3752
3297
  var stage;
3753
3298
  for (;;) {
3754
3299
  var shouldContinue = test();
3755
- if (_isSettledPact$1(shouldContinue)) {
3300
+ if (_isSettledPact$2(shouldContinue)) {
3756
3301
  shouldContinue = shouldContinue.v;
3757
3302
  }
3758
3303
  if (!shouldContinue) {
@@ -3764,7 +3309,7 @@
3764
3309
  }
3765
3310
  var result = body();
3766
3311
  if (result && result.then) {
3767
- if (_isSettledPact$1(result)) {
3312
+ if (_isSettledPact$2(result)) {
3768
3313
  result = result.s;
3769
3314
  } else {
3770
3315
  stage = 1;
@@ -3773,14 +3318,14 @@
3773
3318
  }
3774
3319
  if (update) {
3775
3320
  var updateValue = update();
3776
- if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3321
+ if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
3777
3322
  stage = 2;
3778
3323
  break;
3779
3324
  }
3780
3325
  }
3781
3326
  }
3782
- var pact = new _Pact$1();
3783
- var reject = _settle$1.bind(null, pact, 2);
3327
+ var pact = new _Pact$2();
3328
+ var reject = _settle$2.bind(null, pact, 2);
3784
3329
  (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
3785
3330
  return pact;
3786
3331
  function _resumeAfterBody(value) {
@@ -3788,14 +3333,14 @@
3788
3333
  do {
3789
3334
  if (update) {
3790
3335
  updateValue = update();
3791
- if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3336
+ if (updateValue && updateValue.then && !_isSettledPact$2(updateValue)) {
3792
3337
  updateValue.then(_resumeAfterUpdate).then(void 0, reject);
3793
3338
  return;
3794
3339
  }
3795
3340
  }
3796
3341
  shouldContinue = test();
3797
- if (!shouldContinue || _isSettledPact$1(shouldContinue) && !shouldContinue.v) {
3798
- _settle$1(pact, 1, result);
3342
+ if (!shouldContinue || _isSettledPact$2(shouldContinue) && !shouldContinue.v) {
3343
+ _settle$2(pact, 1, result);
3799
3344
  return;
3800
3345
  }
3801
3346
  if (shouldContinue.then) {
@@ -3803,7 +3348,7 @@
3803
3348
  return;
3804
3349
  }
3805
3350
  result = body();
3806
- if (_isSettledPact$1(result)) {
3351
+ if (_isSettledPact$2(result)) {
3807
3352
  result = result.v;
3808
3353
  }
3809
3354
  } while (!result || !result.then);
@@ -3818,7 +3363,7 @@
3818
3363
  _resumeAfterBody(result);
3819
3364
  }
3820
3365
  } else {
3821
- _settle$1(pact, 1, result);
3366
+ _settle$2(pact, 1, result);
3822
3367
  }
3823
3368
  }
3824
3369
  function _resumeAfterUpdate() {
@@ -3829,7 +3374,7 @@
3829
3374
  _resumeAfterTest(shouldContinue);
3830
3375
  }
3831
3376
  } else {
3832
- _settle$1(pact, 1, result);
3377
+ _settle$2(pact, 1, result);
3833
3378
  }
3834
3379
  }
3835
3380
  }
@@ -3849,7 +3394,7 @@
3849
3394
  do {
3850
3395
  var result = body();
3851
3396
  if (result && result.then) {
3852
- if (_isSettledPact$1(result)) {
3397
+ if (_isSettledPact$2(result)) {
3853
3398
  result = result.v;
3854
3399
  } else {
3855
3400
  awaitBody = true;
@@ -3857,22 +3402,22 @@
3857
3402
  }
3858
3403
  }
3859
3404
  var shouldContinue = test();
3860
- if (_isSettledPact$1(shouldContinue)) {
3405
+ if (_isSettledPact$2(shouldContinue)) {
3861
3406
  shouldContinue = shouldContinue.v;
3862
3407
  }
3863
3408
  if (!shouldContinue) {
3864
3409
  return result;
3865
3410
  }
3866
3411
  } while (!shouldContinue.then);
3867
- var pact = new _Pact$1();
3868
- var reject = _settle$1.bind(null, pact, 2);
3412
+ var pact = new _Pact$2();
3413
+ var reject = _settle$2.bind(null, pact, 2);
3869
3414
  (awaitBody ? result.then(_resumeAfterBody) : shouldContinue.then(_resumeAfterTest)).then(void 0, reject);
3870
3415
  return pact;
3871
3416
  function _resumeAfterBody(value) {
3872
3417
  result = value;
3873
3418
  for (;;) {
3874
3419
  shouldContinue = test();
3875
- if (_isSettledPact$1(shouldContinue)) {
3420
+ if (_isSettledPact$2(shouldContinue)) {
3876
3421
  shouldContinue = shouldContinue.v;
3877
3422
  }
3878
3423
  if (!shouldContinue) {
@@ -3884,7 +3429,7 @@
3884
3429
  }
3885
3430
  result = body();
3886
3431
  if (result && result.then) {
3887
- if (_isSettledPact$1(result)) {
3432
+ if (_isSettledPact$2(result)) {
3888
3433
  result = result.v;
3889
3434
  } else {
3890
3435
  result.then(_resumeAfterBody).then(void 0, reject);
@@ -3892,14 +3437,14 @@
3892
3437
  }
3893
3438
  }
3894
3439
  }
3895
- _settle$1(pact, 1, result);
3440
+ _settle$2(pact, 1, result);
3896
3441
  }
3897
3442
  function _resumeAfterTest(shouldContinue) {
3898
3443
  if (shouldContinue) {
3899
3444
  do {
3900
3445
  result = body();
3901
3446
  if (result && result.then) {
3902
- if (_isSettledPact$1(result)) {
3447
+ if (_isSettledPact$2(result)) {
3903
3448
  result = result.v;
3904
3449
  } else {
3905
3450
  result.then(_resumeAfterBody).then(void 0, reject);
@@ -3907,381 +3452,1069 @@
3907
3452
  }
3908
3453
  }
3909
3454
  shouldContinue = test();
3910
- if (_isSettledPact$1(shouldContinue)) {
3455
+ if (_isSettledPact$2(shouldContinue)) {
3911
3456
  shouldContinue = shouldContinue.v;
3912
3457
  }
3913
3458
  if (!shouldContinue) {
3914
- _settle$1(pact, 1, result);
3459
+ _settle$2(pact, 1, result);
3915
3460
  return;
3916
3461
  }
3917
3462
  } while (!shouldContinue.then);
3918
3463
  shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3919
3464
  } else {
3920
- _settle$1(pact, 1, result);
3465
+ _settle$2(pact, 1, result);
3466
+ }
3467
+ }
3468
+ }
3469
+ function _forTo$1(array, body, check) {
3470
+ var i = -1,
3471
+ pact,
3472
+ reject;
3473
+ function _cycle(result) {
3474
+ try {
3475
+ while (++i < array.length && (!check || !check())) {
3476
+ result = body(i);
3477
+ if (result && result.then) {
3478
+ if (_isSettledPact$2(result)) {
3479
+ result = result.v;
3480
+ } else {
3481
+ result.then(_cycle, reject || (reject = _settle$2.bind(null, pact = new _Pact$2(), 2)));
3482
+ return;
3483
+ }
3484
+ }
3485
+ }
3486
+ if (pact) {
3487
+ _settle$2(pact, 1, result);
3488
+ } else {
3489
+ pact = result;
3490
+ }
3491
+ } catch (e) {
3492
+ _settle$2(pact || (pact = new _Pact$2()), 2, e);
3493
+ }
3494
+ }
3495
+ _cycle();
3496
+ return pact;
3497
+ }
3498
+ var RobustExternalAPICallerService = /*#__PURE__*/function () {
3499
+ RobustExternalAPICallerService.getStats = function getStats() {
3500
+ return this.statsCollector.getStats();
3501
+ }
3502
+
3503
+ /**
3504
+ * @param bio {string} service name for logging
3505
+ * @param providersData {ExternalApiProvider[]} array of providers
3506
+ * @param [logger] {function} function to be used for logging
3507
+ */;
3508
+ function RobustExternalAPICallerService(bio, providersData, logger) {
3509
+ providersData.forEach(function (provider) {
3510
+ if (!provider.endpoint && provider.endpoint !== "" || !provider.httpMethod) {
3511
+ throw new Error("Wrong format of providers data for: " + JSON.stringify(provider));
3512
+ }
3513
+ });
3514
+
3515
+ // We add niceFactor - just number to order the providers array by. It is helpful to call
3516
+ // less robust APIs only if more robust fails
3517
+ this.providers = providersData;
3518
+ providersData.forEach(function (provider) {
3519
+ return provider.resetNiceFactor();
3520
+ });
3521
+ this.bio = bio;
3522
+ this._logger = Logger.logError;
3523
+ }
3524
+ var _proto = RobustExternalAPICallerService.prototype;
3525
+ /**
3526
+ * Performs data retrieval from external APIs. Tries providers till the data is retrieved.
3527
+ *
3528
+ * @param parametersValues {array} array of values of the parameters for URL query string [and/or body]
3529
+ * @param timeoutMS {number} http timeout to wait for response. If provider has its specific timeout value then it is used
3530
+ * @param [cancelToken] {object|undefined} axios token to force-cancel requests from high-level code
3531
+ * @param [attemptsCount] {number|undefined} number of attempts to be performed
3532
+ * @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
3533
+ * @return {Promise<any>} resolving to retrieved data (or array of results if specific provider requires
3534
+ * several requests. NOTE: we flatten nested arrays - results of each separate request done for the specific provider)
3535
+ * @throws Error if requests to all providers are failed
3536
+ */
3537
+ _proto.callExternalAPI = function callExternalAPI(parametersValues, timeoutMS, cancelToken, attemptsCount, doNotFailForNowData) {
3538
+ if (parametersValues === void 0) {
3539
+ parametersValues = [];
3540
+ }
3541
+ if (timeoutMS === void 0) {
3542
+ timeoutMS = 3500;
3543
+ }
3544
+ if (cancelToken === void 0) {
3545
+ cancelToken = null;
3546
+ }
3547
+ if (attemptsCount === void 0) {
3548
+ attemptsCount = 1;
3549
+ }
3550
+ if (doNotFailForNowData === void 0) {
3551
+ doNotFailForNowData = false;
3552
+ }
3553
+ try {
3554
+ var _this = this;
3555
+ var result;
3556
+ var calculationUuid = concurrentCalculationsMetadataHolder.startCalculation(_this.bio);
3557
+ return Promise.resolve(_finallyRethrows$1(function () {
3558
+ return _catch$6(function () {
3559
+ function _temp5() {
3560
+ var _result2, _result3;
3561
+ if (((_result2 = result) == null ? void 0 : _result2.data) == null) {
3562
+ // TODO: [feature, moderate] looks like we should not fail for null data as it is strange - the provider will fail when processing data internally
3563
+ var error = new Error("Failed to retrieve data. It means all attempts have been failed. DEV: add more attempts to this data retrieval");
3564
+ if (!doNotFailForNowData) {
3565
+ throw error;
3566
+ } else {
3567
+ _this._logger(error, _this.bio + ".callExternalAPI");
3568
+ }
3569
+ }
3570
+ return (_result3 = result) == null ? void 0 : _result3.data;
3571
+ }
3572
+ var i = 0;
3573
+ var _temp4 = _for$1(function () {
3574
+ var _result4, _result5;
3575
+ return (i < attemptsCount || !!((_result4 = result) != null && _result4.shouldBeForceRetried)) && ((_result5 = result) == null ? void 0 : _result5.data) == null;
3576
+ }, function () {
3577
+ return ++i;
3578
+ }, function () {
3579
+ /**
3580
+ * We use rpsFactor to improve re-attempting to call the providers if the last attempt resulted with
3581
+ * the fail due to abused RPSes of some (most part of) providers.
3582
+ * The _performCallAttempt in such a case will return increased rpsFactor inside the result object.
3583
+ */
3584
+ var rpsFactor = result ? result.rpsFactor : RobustExternalAPICallerService.defaultRPSFactor;
3585
+ result = null;
3586
+ var _temp3 = _catch$6(function () {
3587
+ function _temp2() {
3588
+ var _result$errors;
3589
+ if ((_result$errors = result.errors) != null && _result$errors.length) {
3590
+ var errors = result.errors;
3591
+ _this._logger(new Error("Failed at attempt " + i + ". " + errors.length + " errors. Messages: " + safeStringify(errors.map(function (error) {
3592
+ return error.message;
3593
+ })) + ": " + safeStringify(errors) + "."), _this.bio + ".callExternalAPI", "", true);
3594
+ }
3595
+ }
3596
+ var _temp = function (_result6) {
3597
+ if (i === 0 && !((_result6 = result) != null && _result6.shouldBeForceRetried)) {
3598
+ return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt) {
3599
+ result = _this$_performCallAtt;
3600
+ });
3601
+ } else {
3602
+ var maxRps = Math.max.apply(Math, _this.providers.map(function (provider) {
3603
+ var _provider$getRps;
3604
+ return (_provider$getRps = provider.getRps()) != null ? _provider$getRps : 0;
3605
+ }));
3606
+ var waitingTimeMs = maxRps ? 1000 / (maxRps / rpsFactor) : 0;
3607
+ return Promise.resolve(new Promise(function (resolve, reject) {
3608
+ setTimeout(function () {
3609
+ try {
3610
+ var _temp6 = _catch$6(function () {
3611
+ return Promise.resolve(_this._performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData)).then(function (_this$_performCallAtt2) {
3612
+ resolve(_this$_performCallAtt2);
3613
+ });
3614
+ }, function (e) {
3615
+ reject(e);
3616
+ });
3617
+ return Promise.resolve(_temp6 && _temp6.then ? _temp6.then(function () {}) : void 0);
3618
+ } catch (e) {
3619
+ return Promise.reject(e);
3620
+ }
3621
+ }, waitingTimeMs);
3622
+ })).then(function (_Promise) {
3623
+ result = _Promise;
3624
+ });
3625
+ }
3626
+ }();
3627
+ return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
3628
+ }, function (e) {
3629
+ _this._logger(e, _this.bio + ".callExternalAPI", "Failed to perform external providers calling");
3630
+ });
3631
+ if (_temp3 && _temp3.then) return _temp3.then(function () {});
3632
+ });
3633
+ return _temp4 && _temp4.then ? _temp4.then(_temp5) : _temp5(_temp4);
3634
+ }, function (e) {
3635
+ improveAndRethrow(e, _this.bio + ".callExternalAPI");
3636
+ });
3637
+ }, function (_wasThrown, _result) {
3638
+ concurrentCalculationsMetadataHolder.endCalculation(_this.bio, calculationUuid);
3639
+ if (_wasThrown) throw _result;
3640
+ return _result;
3641
+ }));
3642
+ } catch (e) {
3643
+ return Promise.reject(e);
3644
+ }
3645
+ };
3646
+ _proto._performCallAttempt = function _performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData) {
3647
+ try {
3648
+ var _temp15 = function _temp15() {
3649
+ var _data;
3650
+ // 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
3651
+ var shouldBeForceRetried = data == null && countOfRequestsDeclinedByRps > Math.floor(providers.length * 0.5);
3652
+ var rpsMultiplier = shouldBeForceRetried ? RobustExternalAPICallerService.rpsMultiplier : 1;
3653
+ return {
3654
+ data: (_data = data) != null ? _data : null,
3655
+ shouldBeForceRetried: shouldBeForceRetried,
3656
+ rpsFactor: rpsFactor * rpsMultiplier,
3657
+ errors: errors
3658
+ };
3659
+ };
3660
+ var _this2 = this;
3661
+ var providers = _this2._reorderProvidersByNiceFactor();
3662
+ var data = undefined,
3663
+ providerIndex = 0,
3664
+ countOfRequestsDeclinedByRps = 0,
3665
+ errors = [];
3666
+ var _temp14 = _for$1(function () {
3667
+ return !data && providerIndex < providers.length;
3668
+ }, void 0, function () {
3669
+ var provider = providers[providerIndex];
3670
+ if (provider.isRpsExceeded()) {
3671
+ /**
3672
+ * Current provider's RPS is exceeded, so we try next provider. Also, we count such cases to make
3673
+ * a decision about the force-retry need.
3674
+ */
3675
+ ++providerIndex;
3676
+ ++countOfRequestsDeclinedByRps;
3677
+ return;
3678
+ }
3679
+ var _temp13 = _finallyRethrows$1(function () {
3680
+ return _catch$6(function () {
3681
+ var _provider$specificHea;
3682
+ function _temp12() {
3683
+ if (iterationsData.length) {
3684
+ if (httpMethods.length > 1) {
3685
+ data = provider.incorporateIterationsData(iterationsData);
3686
+ } else {
3687
+ data = iterationsData[0];
3688
+ }
3689
+ } else if (!doNotFailForNowData) {
3690
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), "Response data was null for some reason");
3691
+ punishProvider(provider);
3692
+ }
3693
+ }
3694
+ var axiosConfig = _extends({}, cancelToken ? {
3695
+ cancelToken: cancelToken
3696
+ } : {}, {
3697
+ timeout: provider.timeout || timeoutMS,
3698
+ headers: (_provider$specificHea = provider.specificHeaders) != null ? _provider$specificHea : {}
3699
+ });
3700
+ var httpMethods = Array.isArray(provider.httpMethod) ? provider.httpMethod : [provider.httpMethod];
3701
+ var iterationsData = [];
3702
+ var _temp11 = _forTo$1(httpMethods, function (subRequestIndex) {
3703
+ function _temp10() {
3704
+ var responsesDataForPages = responsesForPages.map(function (response) {
3705
+ return provider.getDataByResponse(response, parametersValues, subRequestIndex, iterationsData);
3706
+ });
3707
+ var allData = responsesDataForPages;
3708
+ if (Array.isArray(responsesDataForPages[0])) {
3709
+ allData = responsesDataForPages.flat();
3710
+ } else if (responsesDataForPages.length === 1) {
3711
+ allData = responsesDataForPages[0];
3712
+ }
3713
+ iterationsData.push(allData);
3714
+ }
3715
+ var query = provider.composeQueryString(parametersValues, subRequestIndex);
3716
+ var endpoint = "" + provider.endpoint + query;
3717
+ var axiosParams = [endpoint, axiosConfig];
3718
+ if (["post", "put", "patch"].find(function (method) {
3719
+ return method === httpMethods[subRequestIndex];
3720
+ })) {
3721
+ var _provider$composeBody;
3722
+ var body = (_provider$composeBody = provider.composeBody(parametersValues, subRequestIndex)) != null ? _provider$composeBody : null;
3723
+ axiosParams.splice(1, 0, body);
3724
+ }
3725
+ var pageNumber = 0;
3726
+ var responsesForPages = [];
3727
+ var hasNextPage = provider.doesSupportPagination();
3728
+ var _temp9 = _do(function () {
3729
+ function _temp8() {
3730
+ if (hasNextPage) {
3731
+ hasNextPage = !provider.checkWhetherResponseIsForLastPage(responsesForPages[pageNumber - 1], responsesForPages[pageNumber], pageNumber, subRequestIndex);
3732
+ }
3733
+ pageNumber++;
3734
+ }
3735
+ var _temp7 = function () {
3736
+ if (subRequestIndex === 0 && pageNumber === 0) {
3737
+ provider.actualizeLastCalledTimestamp();
3738
+ return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams))).then(function (_AxiosAdapter$call) {
3739
+ responsesForPages[pageNumber] = _AxiosAdapter$call;
3740
+ RobustExternalAPICallerService.statsCollector.externalServiceCalledWithoutError(provider.getApiGroupId());
3741
+ });
3742
+ } else {
3743
+ if (pageNumber > 0) {
3744
+ var actualizedParams = provider.changeQueryParametersForPageNumber(parametersValues, responsesForPages[pageNumber - 1], pageNumber, subRequestIndex);
3745
+ var _query = provider.composeQueryString(actualizedParams, subRequestIndex);
3746
+ axiosParams[0] = "" + provider.endpoint + _query;
3747
+ }
3748
+ /**
3749
+ * For second and more request we postpone each request to not exceed RPS
3750
+ * of current provider. We use rpsFactor to dynamically increase the rps to avoid
3751
+ * too frequent calls if we continue failing to retrieve the data due to RPS exceeding.
3752
+ * TODO: [dev] test RPS factor logic (units or integration)
3753
+ */
3754
+
3755
+ var waitingTimeMS = provider.getRps() ? 1000 / (provider.getRps() / rpsFactor) : 0;
3756
+ var postponeUntilRpsExceeded = function postponeUntilRpsExceeded(recursionLevel) {
3757
+ if (recursionLevel === void 0) {
3758
+ recursionLevel = 0;
3759
+ }
3760
+ try {
3761
+ return Promise.resolve(postponeExecution(function () {
3762
+ try {
3763
+ var _temp17 = function _temp17(_result8) {
3764
+ if (_exit) return _result8;
3765
+ provider.actualizeLastCalledTimestamp();
3766
+ return Promise.resolve(AxiosAdapter.call.apply(AxiosAdapter, [httpMethods[subRequestIndex]].concat(axiosParams)));
3767
+ };
3768
+ var _exit;
3769
+ var maxCountOfPostponingAttempts = 2;
3770
+ var _temp16 = function () {
3771
+ if (provider.isRpsExceeded() && recursionLevel < maxCountOfPostponingAttempts) {
3772
+ return Promise.resolve(postponeUntilRpsExceeded(recursionLevel + 1)).then(function (_await$postponeUntilR) {
3773
+ _exit = 1;
3774
+ return _await$postponeUntilR;
3775
+ });
3776
+ }
3777
+ }();
3778
+ return Promise.resolve(_temp16 && _temp16.then ? _temp16.then(_temp17) : _temp17(_temp16));
3779
+ } catch (e) {
3780
+ return Promise.reject(e);
3781
+ }
3782
+ }, waitingTimeMS));
3783
+ } catch (e) {
3784
+ return Promise.reject(e);
3785
+ }
3786
+ };
3787
+ return Promise.resolve(postponeUntilRpsExceeded()).then(function (_postponeUntilRpsExce) {
3788
+ responsesForPages[pageNumber] = _postponeUntilRpsExce;
3789
+ });
3790
+ }
3791
+ }();
3792
+ return _temp7 && _temp7.then ? _temp7.then(_temp8) : _temp8(_temp7);
3793
+ }, function () {
3794
+ return !!hasNextPage;
3795
+ });
3796
+ return _temp9 && _temp9.then ? _temp9.then(_temp10) : _temp10(_temp9);
3797
+ });
3798
+ return _temp11 && _temp11.then ? _temp11.then(_temp12) : _temp12(_temp11);
3799
+ }, function (e) {
3800
+ punishProvider(provider);
3801
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(provider.getApiGroupId(), e == null ? void 0 : e.message);
3802
+ errors.push(e);
3803
+ });
3804
+ }, function (_wasThrown2, _result7) {
3805
+ providerIndex++;
3806
+ if (_wasThrown2) throw _result7;
3807
+ return _result7;
3808
+ });
3809
+ if (_temp13 && _temp13.then) return _temp13.then(function () {});
3810
+ });
3811
+ return Promise.resolve(_temp14 && _temp14.then ? _temp14.then(_temp15) : _temp15(_temp14));
3812
+ } catch (e) {
3813
+ return Promise.reject(e);
3814
+ }
3815
+ };
3816
+ _proto._reorderProvidersByNiceFactor = function _reorderProvidersByNiceFactor() {
3817
+ var providersCopy = [].concat(this.providers);
3818
+ return providersCopy.sort(function (p1, p2) {
3819
+ return p2.niceFactor - p1.niceFactor;
3820
+ });
3821
+ };
3822
+ return RobustExternalAPICallerService;
3823
+ }();
3824
+ RobustExternalAPICallerService.statsCollector = new ExternalServicesStatsCollector();
3825
+ RobustExternalAPICallerService.defaultRPSFactor = 1;
3826
+ RobustExternalAPICallerService.rpsMultiplier = 1.05;
3827
+ function punishProvider(provider) {
3828
+ provider.niceFactor = provider.niceFactor - 1;
3829
+ }
3830
+
3831
+ /**
3832
+ * This util helps to avoid duplicated calls to a shared resource.
3833
+ * It tracks is there currently active calculation for the specific cache id and make all other requests
3834
+ * with the same cache id waiting for this active calculation to be finished. When the calculation ends
3835
+ * the resolver allows all the waiting requesters to get the data from cache and start their own calculations.
3836
+ *
3837
+ * This class should be instantiated inside some other service where you need to request some resource concurrently.
3838
+ * Rules:
3839
+ * 1. When you need to make a request inside your main service call 'getCachedOrWaitForCachedOrAcquireLock'
3840
+ * on the instance of this class and await for the result. If the flag allowing to start calculation is true
3841
+ * then you can request data inside your main service. Otherwise you should use the cached data as an another
3842
+ * requester just finished the most resent requesting and there is actual data in the cache that
3843
+ * is returned to you here.
3844
+ * 1.1 Also you can acquire a lock directly if you don't want to get cached data. Use the corresponding method 'acquireLock'.
3845
+ *
3846
+ * 2. If you start requesting (when you successfully acquired the lock) then after receiving the result of your
3847
+ * requesting you should call the 'saveCachedData' so the retrieved data will appear in the cache.
3848
+ *
3849
+ * 3. If you successfully acquired the lock then you should after calling the 'saveCachedData' call
3850
+ * the 'releaseLock' - this is mandatory to release the lock and allow other requesters to perform their requests.
3851
+ * WARNING: If for any reason you forget to call this method then this class instance will wait perpetually for
3852
+ * the lock releasing and all your attempts to request the data will constantly fail. So usually call it
3853
+ * inside the 'finally' block.
3854
+ *
3855
+ * TODO: [tests, critical++] add unit tests - massively used logic and can produce sophisticated concurrency bugs
3856
+ */
3857
+
3858
+ function _settle$1(pact, state, value) {
3859
+ if (!pact.s) {
3860
+ if (value instanceof _Pact$1) {
3861
+ if (value.s) {
3862
+ if (state & 1) {
3863
+ state = value.s;
3864
+ }
3865
+ value = value.v;
3866
+ } else {
3867
+ value.o = _settle$1.bind(null, pact, state);
3868
+ return;
3869
+ }
3870
+ }
3871
+ if (value && value.then) {
3872
+ value.then(_settle$1.bind(null, pact, state), _settle$1.bind(null, pact, 2));
3873
+ return;
3874
+ }
3875
+ pact.s = state;
3876
+ pact.v = value;
3877
+ const observer = pact.o;
3878
+ if (observer) {
3879
+ observer(pact);
3880
+ }
3881
+ }
3882
+ }
3883
+
3884
+ /**
3885
+ * Util class to control access to a resource when it can be called in parallel for the same result.
3886
+ * (E.g. getting today coins-fiat rates from some API).
3887
+ */
3888
+ var _Pact$1 = /*#__PURE__*/function () {
3889
+ function _Pact() {}
3890
+ _Pact.prototype.then = function (onFulfilled, onRejected) {
3891
+ var result = new _Pact();
3892
+ var state = this.s;
3893
+ if (state) {
3894
+ var callback = state & 1 ? onFulfilled : onRejected;
3895
+ if (callback) {
3896
+ try {
3897
+ _settle$1(result, 1, callback(this.v));
3898
+ } catch (e) {
3899
+ _settle$1(result, 2, e);
3900
+ }
3901
+ return result;
3902
+ } else {
3903
+ return this;
3904
+ }
3905
+ }
3906
+ this.o = function (_this) {
3907
+ try {
3908
+ var value = _this.v;
3909
+ if (_this.s & 1) {
3910
+ _settle$1(result, 1, onFulfilled ? onFulfilled(value) : value);
3911
+ } else if (onRejected) {
3912
+ _settle$1(result, 1, onRejected(value));
3913
+ } else {
3914
+ _settle$1(result, 2, value);
3915
+ }
3916
+ } catch (e) {
3917
+ _settle$1(result, 2, e);
3918
+ }
3919
+ };
3920
+ return result;
3921
+ };
3922
+ return _Pact;
3923
+ }();
3924
+ function _isSettledPact$1(thenable) {
3925
+ return thenable instanceof _Pact$1 && thenable.s & 1;
3926
+ }
3927
+ function _for(test, update, body) {
3928
+ var stage;
3929
+ for (;;) {
3930
+ var shouldContinue = test();
3931
+ if (_isSettledPact$1(shouldContinue)) {
3932
+ shouldContinue = shouldContinue.v;
3933
+ }
3934
+ if (!shouldContinue) {
3935
+ return result;
3936
+ }
3937
+ if (shouldContinue.then) {
3938
+ stage = 0;
3939
+ break;
3940
+ }
3941
+ var result = body();
3942
+ if (result && result.then) {
3943
+ if (_isSettledPact$1(result)) {
3944
+ result = result.s;
3945
+ } else {
3946
+ stage = 1;
3947
+ break;
3948
+ }
3949
+ }
3950
+ if (update) {
3951
+ var updateValue = update();
3952
+ if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3953
+ stage = 2;
3954
+ break;
3955
+ }
3956
+ }
3957
+ }
3958
+ var pact = new _Pact$1();
3959
+ var reject = _settle$1.bind(null, pact, 2);
3960
+ (stage === 0 ? shouldContinue.then(_resumeAfterTest) : stage === 1 ? result.then(_resumeAfterBody) : updateValue.then(_resumeAfterUpdate)).then(void 0, reject);
3961
+ return pact;
3962
+ function _resumeAfterBody(value) {
3963
+ result = value;
3964
+ do {
3965
+ if (update) {
3966
+ updateValue = update();
3967
+ if (updateValue && updateValue.then && !_isSettledPact$1(updateValue)) {
3968
+ updateValue.then(_resumeAfterUpdate).then(void 0, reject);
3969
+ return;
3970
+ }
3971
+ }
3972
+ shouldContinue = test();
3973
+ if (!shouldContinue || _isSettledPact$1(shouldContinue) && !shouldContinue.v) {
3974
+ _settle$1(pact, 1, result);
3975
+ return;
3976
+ }
3977
+ if (shouldContinue.then) {
3978
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
3979
+ return;
3980
+ }
3981
+ result = body();
3982
+ if (_isSettledPact$1(result)) {
3983
+ result = result.v;
3984
+ }
3985
+ } while (!result || !result.then);
3986
+ result.then(_resumeAfterBody).then(void 0, reject);
3987
+ }
3988
+ function _resumeAfterTest(shouldContinue) {
3989
+ if (shouldContinue) {
3990
+ result = body();
3991
+ if (result && result.then) {
3992
+ result.then(_resumeAfterBody).then(void 0, reject);
3993
+ } else {
3994
+ _resumeAfterBody(result);
3995
+ }
3996
+ } else {
3997
+ _settle$1(pact, 1, result);
3998
+ }
3999
+ }
4000
+ function _resumeAfterUpdate() {
4001
+ if (shouldContinue = test()) {
4002
+ if (shouldContinue.then) {
4003
+ shouldContinue.then(_resumeAfterTest).then(void 0, reject);
4004
+ } else {
4005
+ _resumeAfterTest(shouldContinue);
4006
+ }
4007
+ } else {
4008
+ _settle$1(pact, 1, result);
4009
+ }
4010
+ }
4011
+ }
4012
+ function _catch$5(body, recover) {
4013
+ try {
4014
+ var result = body();
4015
+ } catch (e) {
4016
+ return recover(e);
4017
+ }
4018
+ if (result && result.then) {
4019
+ return result.then(void 0, recover);
4020
+ }
4021
+ return result;
4022
+ }
4023
+ var CacheAndConcurrentRequestsResolver = /*#__PURE__*/function () {
4024
+ /**
4025
+ * @param bio {string} unique identifier for the exact service
4026
+ * @param cache {Cache} cache
4027
+ * @param cacheTtl {number|null} time to live for cache ms. 0 or null means the cache cannot expire
4028
+ * @param [maxCallAttemptsToWaitForAlreadyRunningRequest=100] {number} number of request allowed to do waiting for
4029
+ * result before we fail the original request. Use custom value only if you need to make the attempts count
4030
+ * and polling interval changes.
4031
+ * @param [timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished=1000] {number}
4032
+ * timeout ms for polling for a result. if you change maxCallAttemptsToWaitForAlreadyRunningRequest
4033
+ * then this parameter maybe also require the custom value.
4034
+ * @param [removeExpiredCacheAutomatically=true] {boolean}
4035
+ */
4036
+ function CacheAndConcurrentRequestsResolver(bio, cache, cacheTtl, removeExpiredCacheAutomatically, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished) {
4037
+ if (removeExpiredCacheAutomatically === void 0) {
4038
+ removeExpiredCacheAutomatically = true;
4039
+ }
4040
+ if (maxCallAttemptsToWaitForAlreadyRunningRequest === void 0) {
4041
+ maxCallAttemptsToWaitForAlreadyRunningRequest = 100;
4042
+ }
4043
+ if (timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished === void 0) {
4044
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished = 1000;
4045
+ }
4046
+ if (cacheTtl != null && cacheTtl < timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished * 2) {
4047
+ /*
4048
+ * During the lifetime of this service e.g. if the data is being retrieved slowly we can get
4049
+ * RACE CONDITION when we constantly retrieve data and during retrieval it is expired, so we are trying
4050
+ * to retrieve it again and again.
4051
+ * We have a protection mechanism that we will wait no more than
4052
+ * maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
4053
+ * but this additional check is aimed to reduce potential loading time for some requests.
4054
+ */
4055
+ throw new Error("DEV: Wrong parameters passed to construct " + bio + " - TTL " + cacheTtl + " should be 2 times greater than " + timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
4056
+ }
4057
+ this._bio = bio;
4058
+ this._cache = cache;
4059
+ this._cacheTtlMs = cacheTtl != null ? cacheTtl : null;
4060
+ this._maxExecutionTimeMs = maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished;
4061
+ this._removeExpiredCacheAutomatically = removeExpiredCacheAutomatically;
4062
+ this._requestsManager = new ManagerOfRequestsToTheSameResource(bio, maxCallAttemptsToWaitForAlreadyRunningRequest, timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished);
4063
+ }
4064
+
4065
+ /**
4066
+ * When using this service this is the major method you should call to get data by cache id.
4067
+ * This method checks is there cached data and ether
4068
+ * - returns you flag that you can start requesting data from the shared resource
4069
+ * - or if there is already started calculation waits until it is finished (removed from this service)
4070
+ * and returns you the retrieved data
4071
+ * - or just returns you the cached data
4072
+ *
4073
+ * 'canStartDataRetrieval' equal true means that the lock was acquired, and you should manually call 'saveCachedData'
4074
+ * if needed and then 'releaseLock' to mark this calculation as finished so other
4075
+ * requesters can take their share of the resource.
4076
+ *
4077
+ * @param cacheId {string}
4078
+ * @return {Promise<({
4079
+ * canStartDataRetrieval: true,
4080
+ * cachedData: any,
4081
+ * lockId: string
4082
+ * }|{
4083
+ * canStartDataRetrieval: false,
4084
+ * cachedData: any
4085
+ * })>}
4086
+ */
4087
+ var _proto = CacheAndConcurrentRequestsResolver.prototype;
4088
+ _proto.getCachedOrWaitForCachedOrAcquireLock = function getCachedOrWaitForCachedOrAcquireLock(cacheId) {
4089
+ try {
4090
+ var _this = this;
4091
+ return Promise.resolve(_catch$5(function () {
4092
+ function _temp2() {
4093
+ var _cached, _cached2;
4094
+ return calculationId ? {
4095
+ canStartDataRetrieval: true,
4096
+ cachedData: (_cached = cached) != null ? _cached : cachedDataBackupIsPresentButExpired,
4097
+ lockId: calculationId
4098
+ } : {
4099
+ canStartDataRetrieval: false,
4100
+ cachedData: (_cached2 = cached) != null ? _cached2 : cachedDataBackupIsPresentButExpired
4101
+ };
4102
+ }
4103
+ var startedAtTimestamp = Date.now();
4104
+ var cached = _this._cache.get(cacheId);
4105
+ var cachedDataBackupIsPresentButExpired = null;
4106
+ if (cached != null && !_this._removeExpiredCacheAutomatically) {
4107
+ var lastUpdateTimestamp = _this._cache.getLastUpdateTimestamp(cacheId);
4108
+ if ((lastUpdateTimestamp != null ? lastUpdateTimestamp : 0) + _this._cacheTtlMs < Date.now()) {
4109
+ /*
4110
+ * Here we are manually clearing 'cached' value retrieved from cache to force the data loading.
4111
+ * But we save its value first to the backup variable to be able to return this value if ongoing
4112
+ * requesting fails.
4113
+ */
4114
+ cachedDataBackupIsPresentButExpired = cached;
4115
+ cached = null;
4116
+ }
4117
+ }
4118
+ var calculationId = null;
4119
+ var isRetrievedCacheExpired = true;
4120
+ var isWaitingForActiveCalculationSucceeded;
4121
+ var weStillHaveSomeTimeToProceedExecution = true;
4122
+ var _temp = _for(function () {
4123
+ return calculationId == null && cached == null && !!isRetrievedCacheExpired && !!weStillHaveSomeTimeToProceedExecution;
4124
+ }, void 0, function () {
4125
+ return Promise.resolve(_this._requestsManager.startCalculationOrWaitForActiveToFinish(cacheId)).then(function (result) {
4126
+ calculationId = typeof result === "string" ? result : null;
4127
+ isWaitingForActiveCalculationSucceeded = typeof result === "boolean" ? result : null;
4128
+ cached = _this._cache.get(cacheId);
4129
+ isRetrievedCacheExpired = isWaitingForActiveCalculationSucceeded && cached == null;
4130
+ weStillHaveSomeTimeToProceedExecution = Date.now() - startedAtTimestamp < _this._maxExecutionTimeMs;
4131
+ });
4132
+ });
4133
+ return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
4134
+ }, function (e) {
4135
+ improveAndRethrow(e, _this._bio + ".getCachedOrWaitForCachedOrAcquireLock");
4136
+ }));
4137
+ } catch (e) {
4138
+ return Promise.reject(e);
4139
+ }
4140
+ }
4141
+ /**
4142
+ * Returns just the current cache value for the given id.
4143
+ * Doesn't wait for the active calculation, doesn't acquire lock, just retrieves the current cache as it is.
4144
+ *
4145
+ * @param cacheId {string}
4146
+ * @return {any}
4147
+ */
4148
+ ;
4149
+ _proto.getCached = function getCached(cacheId) {
4150
+ try {
4151
+ return this._cache.get(cacheId);
4152
+ } catch (e) {
4153
+ improveAndRethrow(e, "getCached");
4154
+ }
4155
+ };
4156
+ _proto._getTtl = function _getTtl() {
4157
+ return this._removeExpiredCacheAutomatically ? this._cacheTtlMs : null;
4158
+ }
4159
+
4160
+ /**
4161
+ * Directly acquires the lock despite on cached data availability.
4162
+ * So if this method returns result === true you can start the data retrieval.
4163
+ *
4164
+ * @param cacheId {string}
4165
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>}
4166
+ */;
4167
+ _proto.acquireLock = function acquireLock(cacheId) {
4168
+ try {
4169
+ var _this2 = this;
4170
+ return Promise.resolve(_catch$5(function () {
4171
+ return Promise.resolve(_this2._requestsManager.acquireLock(cacheId));
4172
+ }, function (e) {
4173
+ improveAndRethrow(e, "acquireLock");
4174
+ }));
4175
+ } catch (e) {
4176
+ return Promise.reject(e);
4177
+ }
4178
+ }
4179
+ /**
4180
+ * This method should be called only if you acquired a lock successfully.
4181
+ *
4182
+ * If the current lock id is not equal to the passed one the passed data will be ignored.
4183
+ * Or you can do the synchronous data merging on your side and pass the
4184
+ * wasDataMergedSynchronouslyWithMostRecentCacheState=true so your data will be stored
4185
+ * despite on the lockId.
4186
+ * WARNING: you should do this only if you are sure you perform the synchronous update.
4187
+ *
4188
+ * @param cacheId {string}
4189
+ * @param lockId {string}
4190
+ * @param data {any}
4191
+ * @param [sessionDependentData=true] {boolean}
4192
+ * @param [wasDataMergedSynchronouslyWithMostRecentCacheState=false]
4193
+ */
4194
+ ;
4195
+ _proto.saveCachedData = function saveCachedData(cacheId, lockId, data, sessionDependentData, wasDataMergedSynchronouslyWithMostRecentCacheState) {
4196
+ if (sessionDependentData === void 0) {
4197
+ sessionDependentData = true;
4198
+ }
4199
+ if (wasDataMergedSynchronouslyWithMostRecentCacheState === void 0) {
4200
+ wasDataMergedSynchronouslyWithMostRecentCacheState = false;
4201
+ }
4202
+ try {
4203
+ if (wasDataMergedSynchronouslyWithMostRecentCacheState || this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
4204
+ /* We save passed data only if the <caller> has the currently acquired lockId.
4205
+ * If the passed lockId is not the active one it means that other code cleared/stopped the lock
4206
+ * acquired by the <caller> recently due to some urgent/more prior changes.
4207
+ *
4208
+ * But we allow user to pass the 'wasDataMergedSynchronouslyWithMostRecentCacheState' flag
4209
+ * that tells us that the user had taken the most recent cache value and merged his new data
4210
+ * with that cached value (AFTER possibly performing async data retrieval). This means that we
4211
+ * can ignore the fact that his lockId is no more relevant and save the passed data
4212
+ * as it is synchronously merged with the most recent cached data. (Synchronously merged means that
4213
+ * the lost update cannot occur during the merge time as JS execute the synchronous functions\
4214
+ * till the end).
4215
+ */
4216
+ if (sessionDependentData) {
4217
+ this._cache.putSessionDependentData(cacheId, data, this._getTtl());
4218
+ } else {
4219
+ this._cache.put(cacheId, data, this._getTtl());
4220
+ }
4221
+ }
4222
+ } catch (e) {
4223
+ improveAndRethrow(e, this._bio + ".saveCachedData");
4224
+ }
4225
+ }
4226
+
4227
+ /**
4228
+ * Should be called then and only then if you successfully acquired a lock with the lock id.
4229
+ *
4230
+ * @param cacheId {string}
4231
+ * @param lockId {string}
4232
+ */;
4233
+ _proto.releaseLock = function releaseLock(cacheId, lockId) {
4234
+ try {
4235
+ if (this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
4236
+ this._requestsManager.finishActiveCalculation(cacheId);
4237
+ }
4238
+ } catch (e) {
4239
+ improveAndRethrow(e, this._bio + ".releaseLock");
4240
+ }
4241
+ }
4242
+
4243
+ /**
4244
+ * Actualized currently present cached data by key. Applies the provided function to the cached data.
4245
+ *
4246
+ * @param cacheId {string} id of cache entry
4247
+ * @param synchronousCurrentCacheProcessor (function|null} synchronous function accepting cache entry. Should return
4248
+ * an object in following format:
4249
+ * {
4250
+ * isModified: boolean,
4251
+ * data: any
4252
+ * }
4253
+ * the flag signals whether data was changed during the processing or not
4254
+ * @param [sessionDependent=true] {boolean} whether to mark the cache entry as session-dependent
4255
+ */;
4256
+ _proto.actualizeCachedData = function actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent) {
4257
+ if (sessionDependent === void 0) {
4258
+ sessionDependent = true;
4259
+ }
4260
+ try {
4261
+ var cached = this._cache.get(cacheId);
4262
+ var result = synchronousCurrentCacheProcessor(cached);
4263
+ if (result != null && result.isModified && (result == null ? void 0 : result.data) != null) {
4264
+ if (sessionDependent) {
4265
+ this._cache.putSessionDependentData(cacheId, result == null ? void 0 : result.data, this._getTtl());
4266
+ } else {
4267
+ this._cache.put(cacheId, result == null ? void 0 : result.data, this._getTtl());
4268
+ }
4269
+
4270
+ /* Here we call the lock releasing to ensure the currently active calculation will be ignored.
4271
+ * This is needed to ensure no 'lost update'.
4272
+ * Lost update can occur if we change data in this method and after that some calculation finishes
4273
+ * having the earlier data as its base to calculate its data set result. And the earlier data
4274
+ * has no changes applied inside this method, so we will lose them.
4275
+ *
4276
+ * This is not so good solution: ideally, we should acquire lock before performing any data updating.
4277
+ * But the goal of this method is to provide an instant ability to update the cached data.
4278
+ * And if we start acquiring the lock here the data update can be postponed significantly.
4279
+ * And this kills the desired nature of this method.
4280
+ * So we better lose some data retrieval (means abusing the resource a bit) than lose
4281
+ * the instant update expected after this method execution.
4282
+ */
4283
+ this._requestsManager.finishActiveCalculation(cacheId);
4284
+ }
4285
+ } catch (e) {
4286
+ improveAndRethrow(e, this._bio + ".actualizeCachedData");
4287
+ }
4288
+ };
4289
+ _proto.invalidate = function invalidate(key) {
4290
+ this._cache.invalidate(key);
4291
+ this._requestsManager.finishActiveCalculation(key);
4292
+ };
4293
+ _proto.invalidateContaining = function invalidateContaining(keyPart) {
4294
+ this._cache.invalidateContaining(keyPart);
4295
+ this._requestsManager.finishAllActiveCalculations(keyPart);
4296
+ };
4297
+ _proto.markAsExpiredButDontRemove = function markAsExpiredButDontRemove(key) {
4298
+ if (this._removeExpiredCacheAutomatically) {
4299
+ this._cache.markCacheItemAsExpiredButDontRemove(key, this._cacheTtlMs);
4300
+ } else {
4301
+ this._cache.setLastUpdateTimestamp(key, Date.now() - this._cacheTtlMs - 1);
4302
+ }
4303
+ this._requestsManager.finishAllActiveCalculations(key);
4304
+ };
4305
+ return CacheAndConcurrentRequestsResolver;
4306
+ }();
4307
+ var ManagerOfRequestsToTheSameResource = /*#__PURE__*/function () {
4308
+ /**
4309
+ * @param bio {string} resource-related identifier for logging
4310
+ * @param [maxPollsCount=100] {number} max number of attempts to wait when waiting for a lock acquisition
4311
+ * @param [timeoutDuration=1000] {number} timeout between the polls for a lock acquisition
4312
+ */
4313
+ function ManagerOfRequestsToTheSameResource(bio, maxPollsCount, timeoutDuration) {
4314
+ if (maxPollsCount === void 0) {
4315
+ maxPollsCount = 100;
4316
+ }
4317
+ if (timeoutDuration === void 0) {
4318
+ timeoutDuration = 1000;
4319
+ }
4320
+ this.bio = bio;
4321
+ this.maxPollsCount = maxPollsCount;
4322
+ this.timeoutDuration = timeoutDuration;
4323
+ this._activeCalculationsIds = new Map();
4324
+ this._nextCalculationIds = new Map();
4325
+ }
4326
+
4327
+ /**
4328
+ * If there is no active calculation just creates uuid and returns it.
4329
+ * If there is active calculation waits until it removed from the active calculation uuid variable.
4330
+ *
4331
+ * @param requestHash {string}
4332
+ * @return {Promise<string|boolean>} returns uuid of new active calculation or true if waiting for active
4333
+ * calculation succeed or false if max attempts count exceeded
4334
+ */
4335
+ var _proto2 = ManagerOfRequestsToTheSameResource.prototype;
4336
+ _proto2.startCalculationOrWaitForActiveToFinish = function startCalculationOrWaitForActiveToFinish(requestHash) {
4337
+ try {
4338
+ var _exit;
4339
+ var _this3 = this;
4340
+ var _temp3 = _catch$5(function () {
4341
+ var activeCalculationIdForHash = _this3._activeCalculationsIds.get(requestHash);
4342
+ if (activeCalculationIdForHash == null) {
4343
+ var id = uuid.v4();
4344
+ _this3._activeCalculationsIds.set(requestHash, id);
4345
+ _exit = 1;
4346
+ return id;
4347
+ }
4348
+ return Promise.resolve(_this3._waitForCalculationIdToFinish(requestHash, activeCalculationIdForHash, 0)).then(function (_await$_this3$_waitFo) {
4349
+ _exit = 1;
4350
+ return _await$_this3$_waitFo;
4351
+ });
4352
+ }, function (e) {
4353
+ Logger.logError(e, "startCalculationOrWaitForActiveToFinish_" + _this3.bio);
4354
+ });
4355
+ return Promise.resolve(_temp3 && _temp3.then ? _temp3.then(function (_result3) {
4356
+ return _exit ? _result3 : null;
4357
+ }) : _exit ? _temp3 : null);
4358
+ } catch (e) {
4359
+ return Promise.reject(e);
3921
4360
  }
3922
4361
  }
3923
- }
3924
- function _forTo$1(array, body, check) {
3925
- var i = -1,
3926
- pact,
3927
- reject;
3928
- function _cycle(result) {
4362
+ /**
4363
+ * Acquires lock to the resource by the provided hash.
4364
+ *
4365
+ * @param requestHash {string}
4366
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>} result is true if the lock is successfully
4367
+ * acquired, false if the max allowed time to wait for acquisition expired or any unexpected error occurs
4368
+ * during the waiting.
4369
+ */
4370
+ ;
4371
+ _proto2.acquireLock = function acquireLock(requestHash) {
3929
4372
  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
- }
4373
+ var _this4 = this;
4374
+ return Promise.resolve(_catch$5(function () {
4375
+ var _this4$_nextCalculati;
4376
+ var activeId = _this4._activeCalculationsIds.get(requestHash);
4377
+ var nextId = uuid.v4();
4378
+ if (activeId == null) {
4379
+ _this4._activeCalculationsIds.set(requestHash, nextId);
4380
+ return {
4381
+ result: true,
4382
+ lockId: nextId
4383
+ };
3939
4384
  }
3940
- }
3941
- if (pact) {
3942
- _settle$1(pact, 1, result);
3943
- } else {
3944
- pact = result;
3945
- }
4385
+ var currentNext = (_this4$_nextCalculati = _this4._nextCalculationIds.get(requestHash)) != null ? _this4$_nextCalculati : [];
4386
+ currentNext.push(nextId);
4387
+ _this4._nextCalculationIds.set(requestHash, currentNext);
4388
+ return Promise.resolve(_this4._waitForCalculationIdToFinish(requestHash, activeId, 0, nextId)).then(function (waitingResult) {
4389
+ return {
4390
+ result: waitingResult,
4391
+ lockId: waitingResult ? nextId : undefined
4392
+ };
4393
+ });
4394
+ }, function (e) {
4395
+ improveAndRethrow(e, "acquireLock");
4396
+ }));
3946
4397
  } catch (e) {
3947
- _settle$1(pact || (pact = new _Pact$1()), 2, e);
4398
+ return Promise.reject(e);
3948
4399
  }
3949
4400
  }
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
4401
  /**
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
4402
+ * Clears active calculation id.
4403
+ * WARNING: if you forget to call this method the start* one will perform maxPollsCount attempts before finishing
4404
+ * @param requestHash {string} hash of request. Helps to distinct the request for the same resource but
4405
+ * having different request parameters and hold a dedicated calculation id per this hash
3991
4406
  */
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;
4407
+ ;
4408
+ _proto2.finishActiveCalculation = function finishActiveCalculation(requestHash) {
4409
+ if (requestHash === void 0) {
4410
+ requestHash = "default";
3998
4411
  }
3999
- if (cancelToken === void 0) {
4000
- cancelToken = null;
4412
+ try {
4413
+ var _this$_nextCalculatio;
4414
+ this._activeCalculationsIds["delete"](requestHash);
4415
+ var next = (_this$_nextCalculatio = this._nextCalculationIds.get(requestHash)) != null ? _this$_nextCalculatio : [];
4416
+ if (next.length) {
4417
+ this._activeCalculationsIds.set(requestHash, next[0]);
4418
+ this._nextCalculationIds.set(requestHash, next.slice(1));
4419
+ }
4420
+ } catch (e) {
4421
+ improveAndRethrow(e, "finishActiveCalculation");
4001
4422
  }
4002
- if (attemptsCount === void 0) {
4003
- attemptsCount = 1;
4423
+ };
4424
+ _proto2.finishAllActiveCalculations = function finishAllActiveCalculations(keyPart) {
4425
+ var _this5 = this;
4426
+ if (keyPart === void 0) {
4427
+ keyPart = "";
4004
4428
  }
4005
- if (doNotFailForNowData === void 0) {
4006
- doNotFailForNowData = false;
4429
+ try {
4430
+ Array.from(this._activeCalculationsIds.keys()).forEach(function (hash) {
4431
+ if (typeof hash === "string" && new RegExp(keyPart).test(hash)) {
4432
+ _this5.finishActiveCalculation(hash);
4433
+ }
4434
+ });
4435
+ } catch (e) {
4436
+ improveAndRethrow(e, "finishAllActiveCalculations");
4007
4437
  }
4438
+ }
4439
+
4440
+ /**
4441
+ * @param requestHash {string}
4442
+ * @param lockId {string}
4443
+ * @return {boolean}
4444
+ */;
4445
+ _proto2.isTheLockActiveOne = function isTheLockActiveOne(requestHash, lockId) {
4008
4446
  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
- }));
4447
+ return this._activeCalculationsIds.get(requestHash) === lockId;
4097
4448
  } catch (e) {
4098
- return Promise.reject(e);
4449
+ improveAndRethrow(e, "isTheLockActiveOne");
4450
+ }
4451
+ }
4452
+
4453
+ /**
4454
+ * @param requestHash {string}
4455
+ * @param activeCalculationId {string|null}
4456
+ * @param [attemptIndex=0] {number}
4457
+ * @param waitForCalculationId {string|null} if you want to wait for an exact id to appear as active then pass this parameter
4458
+ * @return {Promise<boolean>} true
4459
+ * - if the given calculation id is no more an active one
4460
+ * - or it is equal to waitForCalculationId
4461
+ * false
4462
+ * - if waiting period exceeds the max allowed waiting time or unexpected error occurs
4463
+ * @private
4464
+ */;
4465
+ _proto2._waitForCalculationIdToFinish = function _waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex, waitForCalculationId) {
4466
+ if (attemptIndex === void 0) {
4467
+ attemptIndex = 0;
4468
+ }
4469
+ if (waitForCalculationId === void 0) {
4470
+ waitForCalculationId = null;
4099
4471
  }
4100
- };
4101
- _proto._performCallAttempt = function _performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData) {
4102
4472
  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;
4473
+ var _this6 = this;
4474
+ try {
4475
+ if (attemptIndex + 1 > _this6.maxPollsCount) {
4476
+ // Max number of polls for active calculation id change is achieved. So we return false.
4477
+ return Promise.resolve(false);
4133
4478
  }
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);
4479
+ var currentId = _this6._activeCalculationsIds.get(requestHash);
4480
+ if (waitForCalculationId == null ? currentId !== activeCalculationId : currentId === waitForCalculationId) {
4481
+ /* We return true depending on the usage of this function:
4482
+ * 1. if there is calculation id that we should wait for to become an active then we return true only
4483
+ * if this id becomes the active one.
4484
+ *
4485
+ * Theoretically we can fail to wait for the desired calculation id. This can be caused by wrong use of
4486
+ * this service or by any other mistakes/errors. But this waiting function will return false anyway if
4487
+ * the number of polls done exceeds the max allowed.
4488
+ *
4489
+ * 2. if we just wait for the currently active calculation id to be finished then we return true
4490
+ * when we notice that the current active id differs from the original passed into this function.
4491
+ */
4492
+ return Promise.resolve(true);
4493
+ } else {
4494
+ /* The original calculation id is still the active one, so we are scheduling a new attempt to check
4495
+ * whether the active calculation id changed or not in timeoutDuration milliseconds.
4496
+ */
4497
+ var it = _this6;
4498
+ return Promise.resolve(new Promise(function (resolve, reject) {
4499
+ setTimeout(function () {
4500
+ try {
4501
+ resolve(it._waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex + 1));
4502
+ } catch (e) {
4503
+ reject(e);
4179
4504
  }
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));
4505
+ }, _this6.timeoutDuration);
4506
+ }));
4507
+ }
4508
+ } catch (e) {
4509
+ Logger.logError(e, "_waitForCalculationIdToFinish", "Failed to wait for active calculation id change.");
4510
+ return Promise.resolve(false);
4511
+ }
4267
4512
  } catch (e) {
4268
4513
  return Promise.reject(e);
4269
4514
  }
4270
4515
  };
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;
4516
+ return ManagerOfRequestsToTheSameResource;
4278
4517
  }();
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
4518
 
4286
4519
  /**
4287
4520
  * Extended edit of RobustExternalApiCallerService supporting cache and management of concurrent requests
@@ -4289,7 +4522,7 @@
4289
4522
  * TODO: [tests, critical] Massively used logic
4290
4523
  */
4291
4524
 
4292
- function _catch$3(body, recover) {
4525
+ function _catch$4(body, recover) {
4293
4526
  try {
4294
4527
  var result = body();
4295
4528
  } catch (e) {
@@ -4386,7 +4619,7 @@
4386
4619
  var cacheId;
4387
4620
  var result;
4388
4621
  return Promise.resolve(_finallyRethrows(function () {
4389
- return _catch$3(function () {
4622
+ return _catch$4(function () {
4390
4623
  cacheId = _this._calculateCacheId(parametersValues, customHashFunctionForParams);
4391
4624
  return Promise.resolve(_this._cahceAndRequestsResolver.getCachedOrWaitForCachedOrAcquireLock(cacheId)).then(function (_this$_cahceAndReques) {
4392
4625
  var _result2, _result4;
@@ -4434,26 +4667,123 @@
4434
4667
  var cacheId = this._calculateCacheId(params, customHashFunctionForParams);
4435
4668
  this._cahceAndRequestsResolver.actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent);
4436
4669
  };
4437
- _proto.markCacheAsExpiredButDontRemove = function markCacheAsExpiredButDontRemove(parametersValues, customHashFunctionForParams) {
4438
- try {
4439
- this._cahceAndRequestsResolver.markAsExpiredButDontRemove(this._calculateCacheId(parametersValues, customHashFunctionForParams));
4440
- } catch (e) {
4441
- improveAndRethrow(e, "markCacheAsExpiredButDontRemove");
4442
- }
4670
+ _proto.markCacheAsExpiredButDontRemove = function markCacheAsExpiredButDontRemove(parametersValues, customHashFunctionForParams) {
4671
+ try {
4672
+ this._cahceAndRequestsResolver.markAsExpiredButDontRemove(this._calculateCacheId(parametersValues, customHashFunctionForParams));
4673
+ } catch (e) {
4674
+ improveAndRethrow(e, "markCacheAsExpiredButDontRemove");
4675
+ }
4676
+ };
4677
+ _proto._calculateCacheId = function _calculateCacheId(parametersValues, customHashFunctionForParams) {
4678
+ if (customHashFunctionForParams === void 0) {
4679
+ customHashFunctionForParams = null;
4680
+ }
4681
+ try {
4682
+ var hash = typeof customHashFunctionForParams === "function" ? customHashFunctionForParams(parametersValues) : !parametersValues ? "" : new Hashes__default["default"].SHA512().hex(safeStringify(parametersValues));
4683
+ return this._provider.bio + "-" + hash;
4684
+ } catch (e) {
4685
+ improveAndRethrow(e, this._provider.bio + "_calculateCacheId");
4686
+ }
4687
+ };
4688
+ return CachedRobustExternalApiCallerService;
4689
+ }();
4690
+
4691
+ function _catch$3(body, recover) {
4692
+ try {
4693
+ var result = body();
4694
+ } catch (e) {
4695
+ return recover(e);
4696
+ }
4697
+ if (result && result.then) {
4698
+ return result.then(void 0, recover);
4699
+ }
4700
+ return result;
4701
+ }
4702
+ var BigdatacloudIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider) {
4703
+ _inheritsLoose(BigdatacloudIpAddressProvider, _ExternalApiProvider);
4704
+ function BigdatacloudIpAddressProvider() {
4705
+ return _ExternalApiProvider.call(this, "https://api.bigdatacloud.net/data/client-ip", "get", 15000, ApiGroups.BIGDATACLOUD) || this;
4706
+ }
4707
+ var _proto = BigdatacloudIpAddressProvider.prototype;
4708
+ _proto.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4709
+ var _response$data;
4710
+ return (response == null ? void 0 : response.data) && ((_response$data = response.data) == null ? void 0 : _response$data.ipString);
4711
+ };
4712
+ return BigdatacloudIpAddressProvider;
4713
+ }(ExternalApiProvider);
4714
+ var TrackipIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider2) {
4715
+ _inheritsLoose(TrackipIpAddressProvider, _ExternalApiProvider2);
4716
+ function TrackipIpAddressProvider() {
4717
+ return _ExternalApiProvider2.call(this, "https://www.trackip.net/ip", "get", 15000, ApiGroups.TRACKIP) || this;
4718
+ }
4719
+ var _proto2 = TrackipIpAddressProvider.prototype;
4720
+ _proto2.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4721
+ return response == null ? void 0 : response.data;
4722
+ };
4723
+ return TrackipIpAddressProvider;
4724
+ }(ExternalApiProvider);
4725
+ var IpifyV6IpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider3) {
4726
+ _inheritsLoose(IpifyV6IpAddressProvider, _ExternalApiProvider3);
4727
+ function IpifyV6IpAddressProvider() {
4728
+ return _ExternalApiProvider3.call(this, "https://api6.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY) || this;
4729
+ }
4730
+ var _proto3 = IpifyV6IpAddressProvider.prototype;
4731
+ _proto3.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4732
+ var _response$data2;
4733
+ return (response == null ? void 0 : response.data) && ((_response$data2 = response.data) == null ? void 0 : _response$data2.ip);
4734
+ };
4735
+ return IpifyV6IpAddressProvider;
4736
+ }(ExternalApiProvider);
4737
+ var IpifyIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider4) {
4738
+ _inheritsLoose(IpifyIpAddressProvider, _ExternalApiProvider4);
4739
+ function IpifyIpAddressProvider() {
4740
+ return _ExternalApiProvider4.call(this, "https://api.ipify.org/?format=json", "get", 15000, ApiGroups.IPIFY) || this;
4741
+ }
4742
+ var _proto4 = IpifyIpAddressProvider.prototype;
4743
+ _proto4.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4744
+ var _response$data3;
4745
+ return (response == null ? void 0 : response.data) && ((_response$data3 = response.data) == null ? void 0 : _response$data3.ip);
4443
4746
  };
4444
- _proto._calculateCacheId = function _calculateCacheId(parametersValues, customHashFunctionForParams) {
4445
- if (customHashFunctionForParams === void 0) {
4446
- customHashFunctionForParams = null;
4447
- }
4747
+ return IpifyIpAddressProvider;
4748
+ }(ExternalApiProvider);
4749
+ var WhatismyipaddressIpAddressProvider = /*#__PURE__*/function (_ExternalApiProvider5) {
4750
+ _inheritsLoose(WhatismyipaddressIpAddressProvider, _ExternalApiProvider5);
4751
+ function WhatismyipaddressIpAddressProvider() {
4752
+ return _ExternalApiProvider5.call(this, "http://bot.whatismyipaddress.com/", "get", 15000, ApiGroups.WHATISMYIPADDRESS) || this;
4753
+ }
4754
+ var _proto5 = WhatismyipaddressIpAddressProvider.prototype;
4755
+ _proto5.getDataByResponse = function getDataByResponse(response, params, subRequestIndex, iterationsData) {
4756
+ return response == null ? void 0 : response.data;
4757
+ };
4758
+ return WhatismyipaddressIpAddressProvider;
4759
+ }(ExternalApiProvider);
4760
+ var IpAddressProvider = /*#__PURE__*/function () {
4761
+ function IpAddressProvider() {}
4762
+ /**
4763
+ * Returns current public IP address identified by one of external services.
4764
+ *
4765
+ * It is easier than manual identification and also (as ip needed for server side to check it) it saves us from
4766
+ * issues related to changes of infrastructure configurations (like adding proxies etc.) so we should not configure
4767
+ * anything on server side to get correct client's IP.
4768
+ *
4769
+ * @returns {Promise<String>} IP address
4770
+ * @throws {Error} if fails to retrieve IP address from all the services
4771
+ */
4772
+ IpAddressProvider.getClientIpAddress = function getClientIpAddress() {
4448
4773
  try {
4449
- var hash = typeof customHashFunctionForParams === "function" ? customHashFunctionForParams(parametersValues) : !parametersValues ? "" : new Hashes__default["default"].SHA512().hex(safeStringify(parametersValues));
4450
- return this._provider.bio + "-" + hash;
4774
+ var _this = this;
4775
+ return Promise.resolve(_catch$3(function () {
4776
+ return Promise.resolve(_this.externalIPAddressAPICaller.callExternalAPICached([], 7000));
4777
+ }, function (e) {
4778
+ improveAndRethrow(e, "getClientIpAddress");
4779
+ }));
4451
4780
  } catch (e) {
4452
- improveAndRethrow(e, this._provider.bio + "_calculateCacheId");
4781
+ return Promise.reject(e);
4453
4782
  }
4454
4783
  };
4455
- return CachedRobustExternalApiCallerService;
4784
+ return IpAddressProvider;
4456
4785
  }();
4786
+ IpAddressProvider.externalIPAddressAPICaller = new CachedRobustExternalApiCallerService("externalIPAddressAPICaller", new Cache(EventBusInstance__default["default"]), [new BigdatacloudIpAddressProvider(), new TrackipIpAddressProvider(), new IpifyV6IpAddressProvider(), new IpifyIpAddressProvider(), new WhatismyipaddressIpAddressProvider()], 300000);
4457
4787
 
4458
4788
  /**
4459
4789
  * Utils class needed to perform cancelling of axios request inside some process.
@@ -4481,239 +4811,6 @@
4481
4811
  return CancelProcessing;
4482
4812
  }();
4483
4813
 
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
4814
  var ExistingSwap =
4718
4815
  /**
4719
4816
  * @param swapId {string}
@@ -6705,6 +6802,7 @@
6705
6802
  exports.ExistingSwapWithFiatData = ExistingSwapWithFiatData;
6706
6803
  exports.ExternalApiProvider = ExternalApiProvider;
6707
6804
  exports.FiatCurrenciesService = FiatCurrenciesService;
6805
+ exports.IpAddressProvider = IpAddressProvider;
6708
6806
  exports.LoadingDots = LoadingDots;
6709
6807
  exports.Logger = Logger;
6710
6808
  exports.LogsStorage = LogsStorage;