@rabbitio/ui-kit 1.0.0-beta.9 → 1.0.0-beta.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/.gitlab-ci.yml +29 -0
  2. package/.husky/commit-msg +19 -0
  3. package/.husky/pre-push +1 -0
  4. package/CHANGELOG.md +0 -0
  5. package/README.md +27 -18
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +16765 -0
  9. package/coverage/coverage-final.json +116 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +1001 -0
  12. package/coverage/prettify.css +1 -0
  13. package/coverage/prettify.js +2 -0
  14. package/coverage/rabbit-ui-kit/index.html +116 -0
  15. package/coverage/rabbit-ui-kit/index.js.html +88 -0
  16. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +148 -0
  17. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +146 -0
  18. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +169 -0
  19. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +199 -0
  20. package/coverage/rabbit-ui-kit/src/common-apis/adapters/axiosAdapter.js.html +190 -0
  21. package/coverage/rabbit-ui-kit/src/common-apis/adapters/index.html +131 -0
  22. package/coverage/rabbit-ui-kit/src/common-apis/adapters/qrUtils.js.html +139 -0
  23. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/apiGroups.js.html +250 -0
  24. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/emailAPI.js.html +133 -0
  25. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/index.html +146 -0
  26. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +352 -0
  27. package/coverage/rabbit-ui-kit/src/common-apis/globalConstants.jsx.html +94 -0
  28. package/coverage/rabbit-ui-kit/src/common-apis/index.html +116 -0
  29. package/coverage/rabbit-ui-kit/src/common-apis/models/blockchain.js.html +115 -0
  30. package/coverage/rabbit-ui-kit/src/common-apis/models/coin.js.html +829 -0
  31. package/coverage/rabbit-ui-kit/src/common-apis/models/index.html +146 -0
  32. package/coverage/rabbit-ui-kit/src/common-apis/models/protocol.js.html +100 -0
  33. package/coverage/rabbit-ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +544 -0
  34. package/coverage/rabbit-ui-kit/src/common-apis/services/index.html +116 -0
  35. package/coverage/rabbit-ui-kit/src/common-apis/utils/amountUtils.js.html +1162 -0
  36. package/coverage/rabbit-ui-kit/src/common-apis/utils/cache.js.html +811 -0
  37. package/coverage/rabbit-ui-kit/src/common-apis/utils/errorUtils.js.html +211 -0
  38. package/coverage/rabbit-ui-kit/src/common-apis/utils/index.html +191 -0
  39. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/index.html +131 -0
  40. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logger.js.html +208 -0
  41. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logsStorage.js.html +268 -0
  42. package/coverage/rabbit-ui-kit/src/common-apis/utils/postponeExecution.js.html +118 -0
  43. package/coverage/rabbit-ui-kit/src/common-apis/utils/rabbitTicker.js.html +157 -0
  44. package/coverage/rabbit-ui-kit/src/common-apis/utils/safeStringify.js.html +235 -0
  45. package/coverage/rabbit-ui-kit/src/index.html +116 -0
  46. package/coverage/rabbit-ui-kit/src/index.js.html +376 -0
  47. package/coverage/rabbit-ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1570 -0
  48. package/coverage/rabbit-ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +526 -0
  49. package/coverage/rabbit-ui-kit/src/robust-api-caller/cancelProcessing.js.html +172 -0
  50. package/coverage/rabbit-ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +310 -0
  51. package/coverage/rabbit-ui-kit/src/robust-api-caller/externalApiProvider.js.html +553 -0
  52. package/coverage/rabbit-ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +319 -0
  53. package/coverage/rabbit-ui-kit/src/robust-api-caller/index.html +206 -0
  54. package/coverage/rabbit-ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +997 -0
  55. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +1825 -0
  56. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +161 -0
  57. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1618 -0
  58. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +1819 -0
  59. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +1861 -0
  60. package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +319 -0
  61. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +514 -0
  62. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +529 -0
  63. package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +176 -0
  64. package/coverage/rabbit-ui-kit/src/swaps-lib/models/partner.js.html +166 -0
  65. package/coverage/rabbit-ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +331 -0
  66. package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
  67. package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +1555 -0
  68. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
  69. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +670 -0
  70. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +124 -0
  71. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +127 -0
  72. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +127 -0
  73. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +106 -0
  74. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +439 -0
  75. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +202 -0
  76. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/index.html +281 -0
  77. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +133 -0
  78. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +346 -0
  79. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +247 -0
  80. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +163 -0
  81. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +268 -0
  82. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +151 -0
  83. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +256 -0
  84. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +116 -0
  85. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +289 -0
  86. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +116 -0
  87. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +187 -0
  88. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +116 -0
  89. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +238 -0
  90. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +116 -0
  91. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +634 -0
  92. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/index.html +116 -0
  93. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +196 -0
  94. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +116 -0
  95. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +277 -0
  96. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +116 -0
  97. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +217 -0
  98. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/index.html +116 -0
  99. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +175 -0
  100. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +116 -0
  101. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +217 -0
  102. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +116 -0
  103. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +529 -0
  104. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/index.html +116 -0
  105. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +508 -0
  106. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +116 -0
  107. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +316 -0
  108. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +116 -0
  109. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +313 -0
  110. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +116 -0
  111. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +202 -0
  112. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/index.html +116 -0
  113. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +712 -0
  114. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +116 -0
  115. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +259 -0
  116. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +116 -0
  117. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +421 -0
  118. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +116 -0
  119. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +415 -0
  120. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +116 -0
  121. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1429 -0
  122. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +116 -0
  123. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1474 -0
  124. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +116 -0
  125. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +211 -0
  126. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +116 -0
  127. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +190 -0
  128. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +116 -0
  129. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +307 -0
  130. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +116 -0
  131. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +496 -0
  132. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +116 -0
  133. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +181 -0
  134. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +116 -0
  135. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +283 -0
  136. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +116 -0
  137. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1567 -0
  138. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +481 -0
  139. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +116 -0
  140. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1747 -0
  141. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +116 -0
  142. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/index.html +116 -0
  143. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +4207 -0
  144. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +116 -0
  145. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +559 -0
  146. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +116 -0
  147. package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +316 -0
  148. package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +116 -0
  149. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/index.html +146 -0
  150. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +151 -0
  151. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +121 -0
  152. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useReferredState.js.html +157 -0
  153. package/coverage/rabbit-ui-kit/src/ui-kit/utils/index.html +161 -0
  154. package/coverage/rabbit-ui-kit/src/ui-kit/utils/inputValueProviders.js.html +235 -0
  155. package/coverage/rabbit-ui-kit/src/ui-kit/utils/textUtils.js.html +139 -0
  156. package/coverage/rabbit-ui-kit/src/ui-kit/utils/uiUtils.js.html +121 -0
  157. package/coverage/rabbit-ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +271 -0
  158. package/coverage/rabbit-ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +202 -0
  159. package/coverage/rabbit-ui-kit/stories/atoms/LinesOfText.stories.jsx.html +283 -0
  160. package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
  161. package/coverage/rabbit-ui-kit/stories/atoms/QrCode.stories.jsx.html +175 -0
  162. package/coverage/rabbit-ui-kit/stories/atoms/RateSelector.stories.jsx.html +136 -0
  163. package/coverage/rabbit-ui-kit/stories/atoms/Validation.stories.jsx.html +178 -0
  164. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +883 -0
  165. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Close.stories.jsx.html +211 -0
  166. package/coverage/rabbit-ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +301 -0
  167. package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +146 -0
  168. package/coverage/rabbit-ui-kit/stories/atoms/index.html +191 -0
  169. package/coverage/rabbit-ui-kit/stories/molecules/AmountInput.stories.jsx.html +289 -0
  170. package/coverage/rabbit-ui-kit/stories/molecules/CoinPicker.stories.jsx.html +322 -0
  171. package/coverage/rabbit-ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +178 -0
  172. package/coverage/rabbit-ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +154 -0
  173. package/coverage/rabbit-ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +235 -0
  174. package/coverage/rabbit-ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +160 -0
  175. package/coverage/rabbit-ui-kit/stories/molecules/index.html +191 -0
  176. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +523 -0
  177. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +328 -0
  178. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/index.html +116 -0
  179. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +337 -0
  180. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/index.html +116 -0
  181. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/index.html +116 -0
  182. package/coverage/rabbit-ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +151 -0
  183. package/coverage/rabbit-ui-kit/stories/organisms/index.html +116 -0
  184. package/coverage/rabbit-ui-kit/stories/stubs/coins.jsx.html +6880 -0
  185. package/coverage/rabbit-ui-kit/stories/stubs/exampleContent.jsx.html +145 -0
  186. package/coverage/rabbit-ui-kit/stories/stubs/index.html +131 -0
  187. package/coverage/rabbit-ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +190 -0
  188. package/coverage/rabbit-ui-kit/stories/templates/index.html +116 -0
  189. package/coverage/sort-arrow-sprite.png +0 -0
  190. package/coverage/sorter.js +196 -0
  191. package/dist/global.css +197 -0
  192. package/dist/global.css.map +1 -0
  193. package/dist/index.cjs +12451 -25
  194. package/dist/index.cjs.map +1 -1
  195. package/dist/index.css +2339 -8491
  196. package/dist/index.css.map +1 -1
  197. package/dist/index.modern.js +9597 -26
  198. package/dist/index.modern.js.map +1 -1
  199. package/dist/index.module.js +12357 -27
  200. package/dist/index.module.js.map +1 -1
  201. package/dist/index.umd.js +12442 -29
  202. package/dist/index.umd.js.map +1 -1
  203. package/package.json +35 -9
  204. package/raw +1000 -0
  205. package/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js +21 -0
  206. package/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js +28 -0
  207. package/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js +38 -0
  208. package/src/common-apis/adapters/axiosAdapter.js +35 -0
  209. package/src/common-apis/adapters/qrUtils.js +18 -0
  210. package/src/common-apis/external-apis/apiGroups.js +55 -0
  211. package/src/common-apis/external-apis/emailAPI.js +16 -0
  212. package/src/common-apis/external-apis/ipAddressProviders.js +89 -0
  213. package/src/common-apis/globalConstants.jsx +3 -0
  214. package/src/common-apis/models/blockchain.js +10 -0
  215. package/src/common-apis/models/coin.js +248 -0
  216. package/src/common-apis/models/protocol.js +5 -0
  217. package/src/{common → common-apis/services}/fiatCurrenciesService.js +4 -12
  218. package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +12 -0
  219. package/src/common-apis/tests/units/utils/amountUtils/composeRateText.test.js +152 -0
  220. package/src/{common → common-apis/utils}/amountUtils.js +72 -136
  221. package/src/common-apis/utils/cache.js +242 -0
  222. package/src/{common → common-apis/utils}/errorUtils.js +15 -0
  223. package/src/common-apis/utils/logging/logger.js +41 -0
  224. package/src/common-apis/utils/logging/logsStorage.js +61 -0
  225. package/src/common-apis/utils/postponeExecution.js +11 -0
  226. package/src/common-apis/utils/rabbitTicker.js +24 -0
  227. package/src/common-apis/utils/safeStringify.js +50 -0
  228. package/src/index.js +96 -9
  229. package/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js +495 -0
  230. package/src/robust-api-caller/cachedRobustExternalApiCallerService.js +147 -0
  231. package/src/robust-api-caller/cancelProcessing.js +29 -0
  232. package/src/robust-api-caller/concurrentCalculationsMetadataHolder.js +75 -0
  233. package/src/robust-api-caller/externalApiProvider.js +156 -0
  234. package/src/robust-api-caller/externalServicesStatsCollector.js +78 -0
  235. package/src/robust-api-caller/robustExternalAPICallerService.js +304 -0
  236. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +533 -0
  237. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +532 -0
  238. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +19 -0
  239. package/src/swaps-lib/external-apis/exolixSwapProvider.js +580 -0
  240. package/src/swaps-lib/external-apis/letsExchangeSwapProvider.js +511 -0
  241. package/src/swaps-lib/external-apis/swapProvider.js +578 -0
  242. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +592 -0
  243. package/src/swaps-lib/models/baseSwapCreationInfo.js +78 -0
  244. package/src/swaps-lib/models/existingSwap.js +143 -0
  245. package/src/swaps-lib/models/existingSwapWithFiatData.js +148 -0
  246. package/src/swaps-lib/models/partner.js +27 -0
  247. package/src/swaps-lib/models/swapProviderCoinInfo.js +82 -0
  248. package/src/swaps-lib/services/publicSwapService.js +490 -0
  249. package/src/swaps-lib/test/external-apis/exolixSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +34 -0
  250. package/src/swaps-lib/test/external-apis/exolixSwapProvider/createSwap.test.js +1043 -0
  251. package/src/swaps-lib/test/external-apis/exolixSwapProvider/getSwapInfo.test.js +611 -0
  252. package/src/swaps-lib/test/external-apis/swapProvider/getAllSupportedCurrencies.test.js +63 -0
  253. package/src/swaps-lib/test/external-apis/swapProvider/getDepositCurrencies.test.js +73 -0
  254. package/src/swaps-lib/test/external-apis/swapProvider/getWithdrawalCurrencies.test.js +102 -0
  255. package/src/swaps-lib/test/external-apis/swapProvider/removeProtocolNameFromCoinName.test.js +152 -0
  256. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +536 -0
  257. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1214 -0
  258. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1707 -0
  259. package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +80 -0
  260. package/src/swaps-lib/utils/swapUtils.js +195 -0
  261. package/{styles → src/ui-kit/assets/styles}/_functions.scss +5 -0
  262. package/{styles → src/ui-kit/assets/styles}/_mixins.scss +2 -2
  263. package/{styles → src/ui-kit/assets/styles}/_placeholder.scss +3 -3
  264. package/{styles → src/ui-kit/assets/styles}/_variables.scss +17 -15
  265. package/src/ui-kit/assets/styles/fonts/NunitoSans-Bold.ttf +0 -0
  266. package/src/ui-kit/assets/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
  267. package/src/ui-kit/assets/styles/fonts/NunitoSans-Light.ttf +0 -0
  268. package/src/ui-kit/assets/styles/fonts/NunitoSans-Regular.ttf +0 -0
  269. package/src/ui-kit/assets/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
  270. package/src/ui-kit/assets/styles/global.scss +171 -0
  271. package/src/ui-kit/assets/styles/index.scss +10 -0
  272. package/src/ui-kit/assets/wrappedImages/arrowIcon.jsx +13 -0
  273. package/src/ui-kit/assets/wrappedImages/arrowTosca.jsx +14 -0
  274. package/src/ui-kit/assets/wrappedImages/arrowWhite.jsx +14 -0
  275. package/src/ui-kit/assets/wrappedImages/darkRectangle.jsx +7 -0
  276. package/src/ui-kit/assets/wrappedImages/determinedError.jsx +118 -0
  277. package/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx +39 -0
  278. package/src/ui-kit/assets/wrappedImages/infoIcon.jsx +16 -0
  279. package/src/ui-kit/assets/wrappedImages/messageIcon.jsx +87 -0
  280. package/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx +54 -0
  281. package/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx +26 -0
  282. package/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx +61 -0
  283. package/src/ui-kit/assets/wrappedImages/walletIcon.jsx +22 -0
  284. package/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx +57 -0
  285. package/src/{components → ui-kit/components}/atoms/AssetIcon/asset-icon.module.scss +1 -1
  286. package/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx +68 -0
  287. package/src/ui-kit/components/atoms/AssetSelection/asset-selection.module.scss +56 -0
  288. package/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx +34 -0
  289. package/src/ui-kit/components/atoms/BackgroundTitle/background-title.module.scss +52 -0
  290. package/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx +51 -0
  291. package/src/ui-kit/components/atoms/InformationMessage/information-message.module.scss +38 -0
  292. package/src/ui-kit/components/atoms/Input/Input.jsx +183 -0
  293. package/src/ui-kit/components/atoms/Input/input.module.scss +107 -0
  294. package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.jsx +8 -28
  295. package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.module.scss +3 -2
  296. package/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx +64 -0
  297. package/src/ui-kit/components/atoms/NoticeIcon/notice-icon.module.scss +14 -0
  298. package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +44 -0
  299. package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +15 -0
  300. package/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx +30 -0
  301. package/src/ui-kit/components/atoms/RateSelector/rate-selector.module.scss +47 -0
  302. package/src/{components → ui-kit/components}/atoms/SupportChat/SupportChat.jsx +5 -1
  303. package/src/ui-kit/components/atoms/Textarea/Textarea.jsx +148 -0
  304. package/src/ui-kit/components/atoms/Textarea/textarea.module.scss +71 -0
  305. package/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx +141 -0
  306. package/src/ui-kit/components/atoms/TitleBox/title-box.module.scss +32 -0
  307. package/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx +77 -0
  308. package/src/ui-kit/components/atoms/Tooltip/tooltip.module.scss +237 -0
  309. package/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx +76 -0
  310. package/src/ui-kit/components/atoms/TwoLinesOfText/lines-of-text.module.scss +65 -0
  311. package/src/ui-kit/components/atoms/Validation/Validation.jsx +39 -0
  312. package/src/ui-kit/components/atoms/Validation/validation.module.scss +19 -0
  313. package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.jsx +24 -50
  314. package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.module.scss +1 -1
  315. package/src/ui-kit/components/atoms/buttons/Close/Close.jsx +58 -0
  316. package/src/ui-kit/components/atoms/buttons/Close/close.module.scss +75 -0
  317. package/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx +112 -0
  318. package/src/ui-kit/components/atoms/buttons/LinkButton/link-button.module.scss +49 -0
  319. package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx +110 -0
  320. package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/radio-button-with-text.module.scss +86 -0
  321. package/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx +448 -0
  322. package/src/ui-kit/components/molecules/AmountInput/amount-input.module.scss +233 -0
  323. package/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx +463 -0
  324. package/src/ui-kit/components/molecules/CoinPicker/coin-picker.module.scss +207 -0
  325. package/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx +42 -0
  326. package/src/ui-kit/components/molecules/ColoredNotice/colored-notice.module.scss +20 -0
  327. package/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx +35 -0
  328. package/src/ui-kit/components/molecules/LineWithIconLink/line-with-icon-link.module.scss +25 -0
  329. package/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx +74 -0
  330. package/src/ui-kit/components/molecules/LogoCarousel/logo-carousel.module.scss +106 -0
  331. package/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx +137 -0
  332. package/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx +32 -0
  333. package/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx +66 -0
  334. package/src/ui-kit/components/organisms/Dialog/Dialog.jsx +494 -0
  335. package/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx +132 -0
  336. package/src/ui-kit/components/organisms/Dialog/DialogButtons/dialog-buttons.module.scss +25 -0
  337. package/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx +554 -0
  338. package/src/ui-kit/components/organisms/Dialog/DialogStep/dialog-step.module.scss +381 -0
  339. package/src/ui-kit/components/organisms/Dialog/dialog.module.scss +226 -0
  340. package/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx +1374 -0
  341. package/src/ui-kit/components/organisms/SwapForm/swap-form.module.scss +134 -0
  342. package/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx +158 -0
  343. package/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx +77 -0
  344. package/src/ui-kit/hooks/useCallHandlingErrors.js +22 -0
  345. package/src/ui-kit/hooks/useIsHydrated.js +12 -0
  346. package/src/ui-kit/hooks/useReferredState.js +24 -0
  347. package/src/ui-kit/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +146 -0
  348. package/src/ui-kit/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +65 -0
  349. package/src/ui-kit/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +104 -0
  350. package/src/ui-kit/utils/inputValueProviders.js +50 -0
  351. package/src/ui-kit/utils/textUtils.js +18 -0
  352. package/src/ui-kit/utils/uiUtils.js +12 -0
  353. package/src/ui-kit/utils/urlQueryUtils.js +62 -0
  354. package/stories/font.scss +40 -0
  355. package/stories/stubs/coins.jsx +2266 -0
  356. package/stories/stubs/exampleContent.jsx +20 -0
  357. package/src/components/atoms/AssetIcon/AssetIcon.jsx +0 -55
  358. package/styles/_global-classes.scss +0 -433
  359. package/styles/fonts/NunitoSans-Bold.ttf +0 -0
  360. package/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
  361. package/styles/fonts/NunitoSans-Light.ttf +0 -0
  362. package/styles/fonts/NunitoSans-Regular.ttf +0 -0
  363. package/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
  364. package/styles/global-styles-index.scss +0 -74
  365. package/styles/index.scss +0 -33
  366. /package/{styles → src/ui-kit/assets/styles}/colors/_light-colors.scss +0 -0
  367. /package/{styles → src/ui-kit/assets/styles}/colors/_solid-colors.scss +0 -0
  368. /package/{styles → src/ui-kit/assets/styles}/size/_margin-size.scss +0 -0
  369. /package/{styles → src/ui-kit/assets/styles}/size/_padding-size.scss +0 -0
@@ -0,0 +1,75 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { Logger } from "../common-apis/utils/logging/logger.js";
4
+
5
+ // TODO: [refactoring, low] Consider removing this logic task_id=c360f2af75764bde8badd9ff1cc00d48
6
+ export class ConcurrentCalculationsMetadataHolder {
7
+ constructor() {
8
+ this._calculations = {};
9
+ }
10
+
11
+ startCalculation(domain, calculationsHistoryMaxLength = 100) {
12
+ if (!this._calculations[domain]) {
13
+ this._calculations[domain] = [];
14
+ }
15
+
16
+ if (this._calculations[domain].length > calculationsHistoryMaxLength) {
17
+ this._calculations[domain] = this._calculations[domain].slice(
18
+ Math.round(calculationsHistoryMaxLength * 0.2)
19
+ );
20
+ }
21
+
22
+ const newCalculation = {
23
+ startTimestamp: Date.now(),
24
+ endTimestamp: null,
25
+ uuid: v4(),
26
+ };
27
+
28
+ this._calculations[domain].push(newCalculation);
29
+
30
+ return newCalculation.uuid;
31
+ }
32
+
33
+ endCalculation(domain, uuid, isFailed = false) {
34
+ try {
35
+ const calculation = this._calculations[domain].find(calculation => calculation?.uuid === uuid);
36
+ if (calculation) {
37
+ calculation.endTimestamp = Date.now();
38
+ calculation.isFiled = isFailed;
39
+ }
40
+
41
+ const elapsed = (((calculation?.endTimestamp ?? 0) - (calculation?.startTimestamp ?? 0)) / 1000).toFixed(1);
42
+ Logger.log("endCalculation", `${elapsed} ms: ${domain}.${(calculation?.uuid ?? "").slice(0, 7)}`);
43
+
44
+ return calculation;
45
+ } catch (e) {
46
+ Logger.logError(e, "endCalculation");
47
+ }
48
+ }
49
+
50
+ isCalculationLate(domain, uuid) {
51
+ const queue = this._calculations[domain];
52
+ const analysingCalculation = queue.find(item => item.uuid === uuid);
53
+ return (
54
+ analysingCalculation &&
55
+ !!queue.find(
56
+ calculation =>
57
+ calculation.endTimestamp != null && calculation.startTimestamp > analysingCalculation.startTimestamp
58
+ )
59
+ );
60
+ }
61
+
62
+ printCalculationsWaitingMoreThanSpecifiedSeconds(waitingLastsMs = 2000) {
63
+ const calculations = Object.keys(this._calculations)
64
+ .map(domain => this._calculations[domain].map(c => ({ ...c, domain })))
65
+ .flat()
66
+ .filter(c => c.endTimestamp === null && Date.now() - c.startTimestamp > waitingLastsMs);
67
+ Logger.log(
68
+ "printCalculationsWaitingMoreThanSpecifiedSeconds",
69
+ `Calculations waiting more than ${(waitingLastsMs / 1000).toFixed(1)}s:\n` +
70
+ calculations.map(c => `${c.domain}.${c.uuid.slice(0, 8)}: ${Date.now() - c.startTimestamp}\n`)
71
+ );
72
+ }
73
+ }
74
+
75
+ export const concurrentCalculationsMetadataHolder = new ConcurrentCalculationsMetadataHolder();
@@ -0,0 +1,156 @@
1
+ export class ExternalApiProvider {
2
+ /**
3
+ * Creates an instance of external api provider.
4
+ *
5
+ * If you need sub-request then use 'subRequestIndex' to check current request index in functions below.
6
+ * Also use array for 'httpMethod'.
7
+ *
8
+ * If the endpoint of dedicated provider has pagination then you should customize the behavior using
9
+ * "changeQueryParametersForPageNumber", "checkWhetherResponseIsForLastPage".
10
+ *
11
+ * We perform RPS counting all over the App to avoid blocking our clients due to abuses of the providers.
12
+ *
13
+ * @param endpoint {string} URL to the provider's endpoint. Note: you can customize it using composeQueryString
14
+ * @param [httpMethod] {string|string[]} one of "get", "post", "put", "patch", "delete" or an array of these values
15
+ * for request having sub-requests
16
+ * @param [timeout] {number} number of milliseconds to wait for the response
17
+ * @param [apiGroup] {ApiGroup} singleton object containing parameters of API group. Helpful when you use the same
18
+ * api for different providers to avoid hardcoding RPS inside each provider what can cause mistakes
19
+ * @param [specificHeaders] {Object} contains specific keys (headers) and values (their content) if needed for this provider
20
+ * @param [maxPageLength] {number} optional number of items per page if the request supports pagination
21
+ */
22
+ constructor(
23
+ endpoint,
24
+ httpMethod,
25
+ timeout,
26
+ apiGroup,
27
+ specificHeaders = {},
28
+ maxPageLength = Number.MAX_SAFE_INTEGER
29
+ ) {
30
+ this.endpoint = endpoint;
31
+ this.httpMethod = httpMethod ?? "get";
32
+ // TODO: [refactoring, critical] We have two timeouts for robust data retrieval - here and inside the service method call, need to remain the only
33
+ this.timeout = timeout ?? 10000;
34
+ // TODO: [refactoring, critical] We need single place for all RPSes as we use them as hardcoded constants now inside different services
35
+ this.apiGroup = apiGroup;
36
+ this.maxPageLength = maxPageLength ?? Number.MAX_SAFE_INTEGER;
37
+ this.niceFactor = 1;
38
+ this.specificHeaders = specificHeaders ?? {};
39
+ }
40
+
41
+ getRps() {
42
+ return this.apiGroup.rps ?? 2;
43
+ }
44
+
45
+ isRpsExceeded() {
46
+ return this.apiGroup.isRpsExceeded();
47
+ }
48
+
49
+ actualizeLastCalledTimestamp() {
50
+ this.apiGroup.actualizeLastCalledTimestamp();
51
+ }
52
+
53
+ getApiGroupId() {
54
+ return this.apiGroup.id;
55
+ }
56
+
57
+ /**
58
+ * Some endpoint can require several sub requests. Example is one request to get confirmed transactions
59
+ * and another request for unconfirmed transactions. You should override this method to return true for such requests.
60
+ *
61
+ * @return {boolean} true if this provider requires several requests to retrieve the data
62
+ */
63
+ doesRequireSubRequests() {
64
+ return false;
65
+ }
66
+
67
+ /**
68
+ * Some endpoint support pagination. Override this method if so and implement corresponding methods.
69
+ *
70
+ * @return {boolean} true if this provider requires several requests to retrieve the data
71
+ */
72
+ doesSupportPagination() {
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * Composes a query string to be added to the endpoint of this provider.
78
+ *
79
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
80
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
81
+ * @returns {string} query string to be concatenated with endpoint
82
+ */
83
+ composeQueryString(params, subRequestIndex = 0) {
84
+ return "";
85
+ }
86
+
87
+ /**
88
+ * Composes a body to be added to the request
89
+ *
90
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
91
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
92
+ * @returns {string}
93
+ */
94
+ composeBody(params, subRequestIndex = 0) {
95
+ return "";
96
+ }
97
+
98
+ /**
99
+ * Extracts data from the response and returns it
100
+ *
101
+ * @param response {Object} HTTP response returned by provider
102
+ * @param [params] {any[]} params array passed to the RobustExternalAPICallerService
103
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
104
+ * @param iterationsData {any[]} array of data retrieved from previous sub-requests
105
+ * @returns {any}
106
+ */
107
+ getDataByResponse(response, params = [], subRequestIndex = 0, iterationsData = []) {
108
+ return [];
109
+ }
110
+
111
+ /**
112
+ * Function changing the query string according to page number and previous response
113
+ * Only for endpoints supporting pagination
114
+ *
115
+ * @param params {any[]} params array passed to the RobustExternalAPICallerService
116
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
117
+ * @param pageNumber {number} new page number. We count from 0. You need to manually increment with 1 if your
118
+ * provider counts pages starting with 1
119
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
120
+ * @returns {any[]}
121
+ */
122
+ changeQueryParametersForPageNumber(params, previousResponse, pageNumber, subRequestIndex = 0) {
123
+ return params;
124
+ }
125
+
126
+ /**
127
+ * Function checking whether the response is for the last page to stop requesting for a next page.
128
+ * Only for endpoints supporting pagination.
129
+ *
130
+ * @param previousResponse {Object} HTTP response returned by provider for previous call (previous page)
131
+ * @param currentResponse {Object} HTTP response returned by provider for current call (current page, next after the previous)
132
+ * @param currentPageNumber {number} current page number (for current response)
133
+ * @param [subRequestIndex] {number} optional number of the sub-request the call is performed for
134
+ * @returns {boolean}
135
+ */
136
+ checkWhetherResponseIsForLastPage(previousResponse, currentResponse, currentPageNumber, subRequestIndex = 0) {
137
+ return true;
138
+ }
139
+
140
+ /**
141
+ * Resets the nice factor to default value
142
+ */
143
+ resetNiceFactor() {
144
+ this.niceFactor = 1;
145
+ }
146
+
147
+ /**
148
+ * Internal method used for requests requiring sub-requests.
149
+ *
150
+ * @param iterationsData {any[]} iterations data retrieved from getDataByResponse called per sub-request.
151
+ * @return {any} by default flatten the passed iterations data array. Should be redefined if you need another logic.
152
+ */
153
+ incorporateIterationsData(iterationsData) {
154
+ return iterationsData.flat();
155
+ }
156
+ }
@@ -0,0 +1,78 @@
1
+ import { improveAndRethrow } from "../common-apis/utils/errorUtils.js";
2
+ import { Logger } from "../common-apis/utils/logging/logger.js";
3
+
4
+ export class ExternalServicesStatsCollector {
5
+ constructor() {
6
+ this.stats = new Map();
7
+ }
8
+
9
+ externalServiceFailed(serviceUrl, message) {
10
+ try {
11
+ const processMessage = (stat, errorMessage) => {
12
+ const errors = stat.errors ?? {};
13
+ errorMessage = errorMessage ?? "";
14
+ if (errorMessage.match(/.*network.+error.*/i)) {
15
+ errors["networkError"] = (errors["networkError"] || 0) + 1;
16
+ } else if (errorMessage.match(/.*timeout.+exceeded.*/i)) {
17
+ errors["timeoutExceeded"] = (errors["timeoutExceeded"] || 0) + 1;
18
+ } else if (errors["other"]) {
19
+ errors["other"].push(message);
20
+ } else {
21
+ errors["other"] = [message];
22
+ }
23
+
24
+ stat.errors = errors;
25
+ };
26
+
27
+ if (this.stats.has(serviceUrl)) {
28
+ const stat = this.stats.get(serviceUrl);
29
+ stat.callsCount += 1;
30
+ stat.failsCount += 1;
31
+ processMessage(stat, message);
32
+ } else {
33
+ this.stats.set(serviceUrl, { callsCount: 1, failsCount: 1 });
34
+ processMessage(this.stats.get(serviceUrl), message);
35
+ }
36
+ } catch (e) {
37
+ improveAndRethrow(e, "externalServiceFailed");
38
+ }
39
+ }
40
+
41
+ externalServiceCalledWithoutError(serviceUrl) {
42
+ try {
43
+ if (this.stats.has(serviceUrl)) {
44
+ const stat = this.stats.get(serviceUrl);
45
+ stat.callsCount += 1;
46
+ } else {
47
+ this.stats.set(serviceUrl, { callsCount: 1, failsCount: 0 });
48
+ }
49
+ } catch (e) {
50
+ improveAndRethrow(e, "externalServiceCalledWithoutError");
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Returns statistics about external services failures.
56
+ * Provides how many calls were performed and what the percent of failed calls. Also returns errors stat.
57
+ *
58
+ * @return {Array<object>} Array of objects of type { failsPerCent: number, calls: number }
59
+ * sorted by the highest fails percent desc
60
+ */
61
+ getStats() {
62
+ try {
63
+ return Array.from(this.stats.keys())
64
+ .map(key => {
65
+ const stat = this.stats.get(key);
66
+ return {
67
+ url: key,
68
+ failsPerCent: ((stat.failsCount / stat.callsCount) * 100).toFixed(2),
69
+ calls: stat.callsCount,
70
+ errors: stat.errors ?? [],
71
+ };
72
+ })
73
+ .sort((s1, s2) => s1.failsPerCent - s2.failsPerCent);
74
+ } catch (e) {
75
+ Logger.logError(e, "getStats");
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,304 @@
1
+ import { improveAndRethrow } from "../common-apis/utils/errorUtils.js";
2
+ import { safeStringify } from "../common-apis/utils/safeStringify.js";
3
+ import { Logger } from "../common-apis/utils/logging/logger.js";
4
+ import { postponeExecution } from "../common-apis/utils/postponeExecution.js";
5
+ import { AxiosAdapter } from "../common-apis/adapters/axiosAdapter.js";
6
+
7
+ import { concurrentCalculationsMetadataHolder } from "./concurrentCalculationsMetadataHolder.js";
8
+ import { ExternalServicesStatsCollector } from "./externalServicesStatsCollector.js";
9
+
10
+ /**
11
+ * TODO: [refactoring, critical] update backend copy of this service. Also there is a task to extract this
12
+ * service and other related to it stuff to dedicated npm package task_id=b008ee5e4a3f42c08c73831c4bb3db4e
13
+ *
14
+ * Template service needed to avoid duplication of the same logic when we need to call
15
+ * external APIs to retrieve some data. The idea is to use several API providers to retrieve the same data. It helps to
16
+ * improve the reliability of a data retrieval.
17
+ */
18
+ export class RobustExternalAPICallerService {
19
+ static statsCollector = new ExternalServicesStatsCollector();
20
+
21
+ static getStats() {
22
+ return this.statsCollector.getStats();
23
+ }
24
+
25
+ /**
26
+ * @param bio {string} service name for logging
27
+ * @param providersData {ExternalApiProvider[]} array of providers
28
+ * @param [logger] {function} function to be used for logging
29
+ */
30
+ constructor(bio, providersData, logger = Logger.logError) {
31
+ providersData.forEach(provider => {
32
+ if ((!provider.endpoint && provider.endpoint !== "") || !provider.httpMethod) {
33
+ throw new Error(`Wrong format of providers data for: ${JSON.stringify(provider)}`);
34
+ }
35
+ });
36
+
37
+ // We add niceFactor - just number to order the providers array by. It is helpful to call
38
+ // less robust APIs only if more robust fails
39
+ this.providers = providersData;
40
+ providersData.forEach(provider => provider.resetNiceFactor());
41
+ this.bio = bio;
42
+ this._logger = (...args) => {
43
+ Logger.logError(...args);
44
+ };
45
+ }
46
+
47
+ static defaultRPSFactor = 1;
48
+ static rpsMultiplier = 1.05;
49
+
50
+ /**
51
+ * Performs data retrieval from external APIs. Tries providers till the data is retrieved.
52
+ *
53
+ * @param parametersValues {array} array of values of the parameters for URL query string [and/or body]
54
+ * @param timeoutMS {number} http timeout to wait for response. If provider has its specific timeout value then it is used
55
+ * @param [cancelToken] {object|undefined} axios token to force-cancel requests from high-level code
56
+ * @param [attemptsCount] {number|undefined} number of attempts to be performed
57
+ * @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
58
+ * @return {Promise<any>} resolving to retrieved data (or array of results if specific provider requires
59
+ * several requests. NOTE: we flatten nested arrays - results of each separate request done for the specific provider)
60
+ * @throws Error if requests to all providers are failed
61
+ */
62
+ async callExternalAPI(
63
+ parametersValues = [],
64
+ timeoutMS = 3500,
65
+ cancelToken = null,
66
+ attemptsCount = 1,
67
+ doNotFailForNowData = false
68
+ ) {
69
+ let result;
70
+ const calculationUuid = concurrentCalculationsMetadataHolder.startCalculation(this.bio);
71
+
72
+ try {
73
+ for (let i = 0; (i < attemptsCount || result?.shouldBeForceRetried) && result?.data == null; ++i) {
74
+ /**
75
+ * We use rpsFactor to improve re-attempting to call the providers if the last attempt resulted with
76
+ * the fail due to abused RPSes of some (most part of) providers.
77
+ * The _performCallAttempt in such a case will return increased rpsFactor inside the result object.
78
+ */
79
+ const rpsFactor = result ? result.rpsFactor : RobustExternalAPICallerService.defaultRPSFactor;
80
+
81
+ result = null;
82
+
83
+ try {
84
+ if (i === 0 && !result?.shouldBeForceRetried) {
85
+ result = await this._performCallAttempt(
86
+ parametersValues,
87
+ timeoutMS,
88
+ cancelToken,
89
+ rpsFactor,
90
+ doNotFailForNowData
91
+ );
92
+ } else {
93
+ const maxRps = Math.max(...this.providers.map(provider => provider.getRps() ?? 0));
94
+ const waitingTimeMs = maxRps ? 1000 / (maxRps / rpsFactor) : 0;
95
+
96
+ result = await new Promise((resolve, reject) => {
97
+ setTimeout(async () => {
98
+ try {
99
+ resolve(
100
+ await this._performCallAttempt(
101
+ parametersValues,
102
+ timeoutMS,
103
+ cancelToken,
104
+ rpsFactor,
105
+ doNotFailForNowData
106
+ )
107
+ );
108
+ } catch (e) {
109
+ reject(e);
110
+ }
111
+ }, waitingTimeMs);
112
+ });
113
+ }
114
+ if (result.errors?.length) {
115
+ const errors = result.errors;
116
+ this._logger(
117
+ new Error(
118
+ `Failed at attempt ${i}. ${errors.length} errors. Messages: ${safeStringify(
119
+ errors.map(error => error.message)
120
+ )}: ${safeStringify(errors)}.`
121
+ ),
122
+ `${this.bio}.callExternalAPI`,
123
+ "",
124
+ true
125
+ );
126
+ }
127
+ } catch (e) {
128
+ this._logger(e, `${this.bio}.callExternalAPI`, "Failed to perform external providers calling");
129
+ }
130
+ }
131
+
132
+ if (result?.data == null) {
133
+ // TODO: [feature, moderate] looks like we should not fail for null data as it is strange - the provider will fail when processing data internally
134
+ const error = new Error(
135
+ `Failed to retrieve data. It means all attempts have been failed. DEV: add more attempts to this data retrieval`
136
+ );
137
+ if (!doNotFailForNowData) {
138
+ throw error;
139
+ } else {
140
+ this._logger(error, `${this.bio}.callExternalAPI`);
141
+ }
142
+ }
143
+
144
+ return result?.data;
145
+ } catch (e) {
146
+ improveAndRethrow(e, `${this.bio}.callExternalAPI`);
147
+ } finally {
148
+ concurrentCalculationsMetadataHolder.endCalculation(this.bio, calculationUuid);
149
+ }
150
+ }
151
+
152
+ async _performCallAttempt(parametersValues, timeoutMS, cancelToken, rpsFactor, doNotFailForNowData) {
153
+ const providers = this._reorderProvidersByNiceFactor();
154
+ let data = undefined,
155
+ providerIndex = 0,
156
+ countOfRequestsDeclinedByRps = 0,
157
+ errors = [];
158
+ while (!data && providerIndex < providers.length) {
159
+ let provider = providers[providerIndex];
160
+ if (provider.isRpsExceeded()) {
161
+ /**
162
+ * Current provider's RPS is exceeded, so we try next provider. Also, we count such cases to make
163
+ * a decision about the force-retry need.
164
+ */
165
+ ++providerIndex;
166
+ ++countOfRequestsDeclinedByRps;
167
+ continue;
168
+ }
169
+
170
+ try {
171
+ const axiosConfig = {
172
+ ...(cancelToken ? { cancelToken } : {}),
173
+ timeout: provider.timeout || timeoutMS,
174
+ headers: provider.specificHeaders ?? {},
175
+ };
176
+ const httpMethods = Array.isArray(provider.httpMethod) ? provider.httpMethod : [provider.httpMethod];
177
+ const iterationsData = [];
178
+ for (let subRequestIndex = 0; subRequestIndex < httpMethods.length; ++subRequestIndex) {
179
+ const query = provider.composeQueryString(parametersValues, subRequestIndex);
180
+ const endpoint = `${provider.endpoint}${query}`;
181
+ const axiosParams = [endpoint, axiosConfig];
182
+ if (["post", "put", "patch"].find(method => method === httpMethods[subRequestIndex])) {
183
+ const body = provider.composeBody(parametersValues, subRequestIndex) ?? null;
184
+ axiosParams.splice(1, 0, body);
185
+ }
186
+
187
+ let pageNumber = 0;
188
+ const responsesForPages = [];
189
+ let hasNextPage = provider.doesSupportPagination();
190
+ do {
191
+ if (subRequestIndex === 0 && pageNumber === 0) {
192
+ provider.actualizeLastCalledTimestamp();
193
+ responsesForPages[pageNumber] = await AxiosAdapter.call(
194
+ httpMethods[subRequestIndex],
195
+ ...axiosParams
196
+ );
197
+ RobustExternalAPICallerService.statsCollector.externalServiceCalledWithoutError(
198
+ provider.getApiGroupId()
199
+ );
200
+ } else {
201
+ if (pageNumber > 0) {
202
+ const actualizedParams = provider.changeQueryParametersForPageNumber(
203
+ parametersValues,
204
+ responsesForPages[pageNumber - 1],
205
+ pageNumber,
206
+ subRequestIndex
207
+ );
208
+ const query = provider.composeQueryString(actualizedParams, subRequestIndex);
209
+ axiosParams[0] = `${provider.endpoint}${query}`;
210
+ }
211
+ /**
212
+ * For second and more request we postpone each request to not exceed RPS
213
+ * of current provider. We use rpsFactor to dynamically increase the rps to avoid
214
+ * too frequent calls if we continue failing to retrieve the data due to RPS exceeding.
215
+ * TODO: [dev] test RPS factor logic (units or integration)
216
+ */
217
+
218
+ const waitingTimeMS = provider.getRps() ? 1000 / (provider.getRps() / rpsFactor) : 0;
219
+
220
+ const postponeUntilRpsExceeded = async (recursionLevel = 0) => {
221
+ return await postponeExecution(async () => {
222
+ const maxCountOfPostponingAttempts = 2;
223
+ if (provider.isRpsExceeded() && recursionLevel < maxCountOfPostponingAttempts) {
224
+ return await postponeUntilRpsExceeded(recursionLevel + 1);
225
+ }
226
+ provider.actualizeLastCalledTimestamp();
227
+ return await AxiosAdapter.call(httpMethods[subRequestIndex], ...axiosParams);
228
+ }, waitingTimeMS);
229
+ };
230
+
231
+ responsesForPages[pageNumber] = await postponeUntilRpsExceeded();
232
+ }
233
+
234
+ if (hasNextPage) {
235
+ hasNextPage = !provider.checkWhetherResponseIsForLastPage(
236
+ responsesForPages[pageNumber - 1],
237
+ responsesForPages[pageNumber],
238
+ pageNumber,
239
+ subRequestIndex
240
+ );
241
+ }
242
+ pageNumber++;
243
+ } while (hasNextPage);
244
+
245
+ const responsesDataForPages = responsesForPages.map(response =>
246
+ provider.getDataByResponse(response, parametersValues, subRequestIndex, iterationsData)
247
+ );
248
+
249
+ let allData = responsesDataForPages;
250
+ if (Array.isArray(responsesDataForPages[0])) {
251
+ allData = responsesDataForPages.flat();
252
+ } else if (responsesDataForPages.length === 1) {
253
+ allData = responsesDataForPages[0];
254
+ }
255
+
256
+ iterationsData.push(allData);
257
+ }
258
+ if (iterationsData.length) {
259
+ if (httpMethods.length > 1) {
260
+ data = provider.incorporateIterationsData(iterationsData);
261
+ } else {
262
+ data = iterationsData[0];
263
+ }
264
+ } else if (!doNotFailForNowData) {
265
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(
266
+ provider.getApiGroupId(),
267
+ "Response data was null for some reason"
268
+ );
269
+ punishProvider(provider);
270
+ }
271
+ } catch (e) {
272
+ punishProvider(provider);
273
+ RobustExternalAPICallerService.statsCollector.externalServiceFailed(
274
+ provider.getApiGroupId(),
275
+ e?.message
276
+ );
277
+ errors.push(e);
278
+ } finally {
279
+ providerIndex++;
280
+ }
281
+ }
282
+
283
+ // 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
284
+ const shouldBeForceRetried = data == null && countOfRequestsDeclinedByRps > Math.floor(providers.length * 0.5);
285
+ const rpsMultiplier = shouldBeForceRetried ? RobustExternalAPICallerService.rpsMultiplier : 1;
286
+
287
+ return {
288
+ data: data ?? null,
289
+ shouldBeForceRetried,
290
+ rpsFactor: rpsFactor * rpsMultiplier,
291
+ errors,
292
+ };
293
+ }
294
+
295
+ _reorderProvidersByNiceFactor() {
296
+ const providersCopy = [...this.providers];
297
+
298
+ return providersCopy.sort((p1, p2) => p2.niceFactor - p1.niceFactor);
299
+ }
300
+ }
301
+
302
+ function punishProvider(provider) {
303
+ provider.niceFactor = provider.niceFactor - 1;
304
+ }