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