@audius/sdk 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +38 -0
- package/.prettierrc.js +1 -0
- package/.python-version +1 -0
- package/Dockerfile +15 -0
- package/README.md +3 -0
- package/babel.config.js +3 -0
- package/data-contracts/ABIs/AdminUpgradeabilityProxy.json +132 -0
- package/data-contracts/ABIs/BaseAdminUpgradeabilityProxy.json +113 -0
- package/data-contracts/ABIs/BaseUpgradeabilityProxy.json +22 -0
- package/data-contracts/ABIs/DiscoveryProviderFactory.json +189 -0
- package/data-contracts/ABIs/DiscoveryProviderFactoryInterface.json +61 -0
- package/data-contracts/ABIs/DiscoveryProviderStorage.json +205 -0
- package/data-contracts/ABIs/DiscoveryProviderStorageInterface.json +65 -0
- package/data-contracts/ABIs/ECDSA.json +4 -0
- package/data-contracts/ABIs/IPLDBlacklistFactory.json +168 -0
- package/data-contracts/ABIs/Initializable.json +4 -0
- package/data-contracts/ABIs/Migrations.json +67 -0
- package/data-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/data-contracts/ABIs/Ownable.json +79 -0
- package/data-contracts/ABIs/PlaylistFactory.json +669 -0
- package/data-contracts/ABIs/PlaylistFactoryInterface.json +42 -0
- package/data-contracts/ABIs/PlaylistStorage.json +250 -0
- package/data-contracts/ABIs/PlaylistStorageInterface.json +129 -0
- package/data-contracts/ABIs/Proxy.json +10 -0
- package/data-contracts/ABIs/Registry.json +240 -0
- package/data-contracts/ABIs/RegistryContract.json +102 -0
- package/data-contracts/ABIs/RegistryContractInterface.json +28 -0
- package/data-contracts/ABIs/RegistryInterface.json +66 -0
- package/data-contracts/ABIs/SigningLogic.json +43 -0
- package/data-contracts/ABIs/SigningLogicInitializable.json +46 -0
- package/data-contracts/ABIs/SocialFeatureFactory.json +460 -0
- package/data-contracts/ABIs/SocialFeatureStorage.json +225 -0
- package/data-contracts/ABIs/SocialFeatureStorageInterface.json +123 -0
- package/data-contracts/ABIs/TestContract.json +135 -0
- package/data-contracts/ABIs/TestContractInterface.json +19 -0
- package/data-contracts/ABIs/TestContractWithStorage.json +165 -0
- package/data-contracts/ABIs/TestContractWithStorageInterface.json +24 -0
- package/data-contracts/ABIs/TestStorage.json +144 -0
- package/data-contracts/ABIs/TestStorageInterface.json +42 -0
- package/data-contracts/ABIs/TestUserReplicaSetManager.json +432 -0
- package/data-contracts/ABIs/TrackFactory.json +391 -0
- package/data-contracts/ABIs/TrackFactoryInterface.json +73 -0
- package/data-contracts/ABIs/TrackStorage.json +223 -0
- package/data-contracts/ABIs/TrackStorageInterface.json +121 -0
- package/data-contracts/ABIs/UpgradeabilityProxy.json +37 -0
- package/data-contracts/ABIs/UserFactory.json +657 -0
- package/data-contracts/ABIs/UserFactoryInterface.json +65 -0
- package/data-contracts/ABIs/UserLibraryFactory.json +334 -0
- package/data-contracts/ABIs/UserReplicaSetManager.json +418 -0
- package/data-contracts/ABIs/UserStorage.json +233 -0
- package/data-contracts/ABIs/UserStorageInterface.json +93 -0
- package/data-contracts/signatureSchemas.ts +1236 -0
- package/dist/core.d.ts +446 -0
- package/dist/core.js +769 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +689 -0
- package/dist/index.js +72850 -0
- package/dist/index.js.map +1 -0
- package/eth-contracts/ABIs/Address.json +4 -0
- package/eth-contracts/ABIs/AudiusAdminUpgradeabilityProxy.json +105 -0
- package/eth-contracts/ABIs/AudiusClaimDistributor.json +4968 -0
- package/eth-contracts/ABIs/AudiusToken.json +724 -0
- package/eth-contracts/ABIs/BaseUpgradeabilityProxy.json +23 -0
- package/eth-contracts/ABIs/Checkpointing.json +4 -0
- package/eth-contracts/ABIs/ClaimsManager.json +539 -0
- package/eth-contracts/ABIs/Context.json +11 -0
- package/eth-contracts/ABIs/DelegateManager.json +989 -0
- package/eth-contracts/ABIs/DelegateManagerV2.json +1049 -0
- package/eth-contracts/ABIs/DelegateManagerV2Bad.json +1049 -0
- package/eth-contracts/ABIs/ERC20.json +252 -0
- package/eth-contracts/ABIs/ERC20Burnable.json +287 -0
- package/eth-contracts/ABIs/ERC20Detailed.json +270 -0
- package/eth-contracts/ABIs/ERC20Mintable.json +364 -0
- package/eth-contracts/ABIs/ERC20Pausable.json +397 -0
- package/eth-contracts/ABIs/EthRewardsManager.json +174 -0
- package/eth-contracts/ABIs/Governance.json +938 -0
- package/eth-contracts/ABIs/GovernanceUpgraded.json +953 -0
- package/eth-contracts/ABIs/GovernanceV2.json +938 -0
- package/eth-contracts/ABIs/IERC20.json +200 -0
- package/eth-contracts/ABIs/Initializable.json +4 -0
- package/eth-contracts/ABIs/InitializableV2.json +14 -0
- package/eth-contracts/ABIs/Migrations.json +71 -0
- package/eth-contracts/ABIs/MinterRole.json +91 -0
- package/eth-contracts/ABIs/MockAccount.json +62 -0
- package/eth-contracts/ABIs/MockDelegateManager.json +55 -0
- package/eth-contracts/ABIs/MockStakingCaller.json +259 -0
- package/eth-contracts/ABIs/MockWormhole.json +106 -0
- package/eth-contracts/ABIs/OpenZeppelinUpgradesAddress.json +4 -0
- package/eth-contracts/ABIs/Ownable.json +93 -0
- package/eth-contracts/ABIs/Pausable.json +150 -0
- package/eth-contracts/ABIs/PauserRole.json +91 -0
- package/eth-contracts/ABIs/Proxy.json +10 -0
- package/eth-contracts/ABIs/Registry.json +288 -0
- package/eth-contracts/ABIs/Roles.json +4 -0
- package/eth-contracts/ABIs/SafeERC20.json +4 -0
- package/eth-contracts/ABIs/SafeMath.json +4 -0
- package/eth-contracts/ABIs/ServiceProviderFactory.json +1153 -0
- package/eth-contracts/ABIs/ServiceTypeManager.json +337 -0
- package/eth-contracts/ABIs/Staking.json +555 -0
- package/eth-contracts/ABIs/StakingUpgraded.json +570 -0
- package/eth-contracts/ABIs/TestContract.json +44 -0
- package/eth-contracts/ABIs/TrustedNotifierManager.json +265 -0
- package/eth-contracts/ABIs/Uint256Helpers.json +4 -0
- package/eth-contracts/ABIs/UpgradeabilityProxy.json +40 -0
- package/eth-contracts/ABIs/Wormhole.json +45 -0
- package/eth-contracts/ABIs/WormholeClient.json +155 -0
- package/examples/file.mp3 +0 -0
- package/examples/initAudiusLibs.js +86 -0
- package/examples/initializeVersions.js +95 -0
- package/examples/pic.jpg +0 -0
- package/initScripts/configureLocalDiscProv.js +167 -0
- package/initScripts/helpers/claim.js +43 -0
- package/initScripts/helpers/distributeTokens.js +24 -0
- package/initScripts/helpers/spRegistration.js +138 -0
- package/initScripts/helpers/utils.js +34 -0
- package/initScripts/helpers/version.js +93 -0
- package/initScripts/local.js +617 -0
- package/initScripts/mainnet.js +131 -0
- package/initScripts/manageProdRelayerWallets.js +191 -0
- package/package.json +125 -0
- package/rollup.config.js +164 -0
- package/scripts/AudiusClaimDistributor.json +4968 -0
- package/scripts/Wormhole.json +155 -0
- package/scripts/addCIDToIpldBlacklist.js +124 -0
- package/scripts/circleci-test.sh +53 -0
- package/scripts/communityRewards/transferCommunityRewardsToSolana.js +222 -0
- package/scripts/ipfs.sh +58 -0
- package/scripts/migrate_contracts.sh +25 -0
- package/scripts/reset.sh +65 -0
- package/scripts/test.sh +77 -0
- package/src/api/account.js +670 -0
- package/src/api/base.js +122 -0
- package/src/api/file.js +168 -0
- package/src/api/playlist.js +328 -0
- package/src/api/rewards.d.ts +4 -0
- package/src/api/rewards.js +682 -0
- package/src/api/serviceProvider.js +154 -0
- package/src/api/track.js +604 -0
- package/src/api/user.js +888 -0
- package/src/api/user.test.js +172 -0
- package/src/constants.ts +7 -0
- package/src/core.ts +3 -0
- package/src/index.js +6 -0
- package/src/libs.d.ts +3 -0
- package/src/libs.js +619 -0
- package/src/sanityChecks/addSecondaries.js +40 -0
- package/src/sanityChecks/assignReplicaSetIfNecessary.js +10 -0
- package/src/sanityChecks/index.d.ts +9 -0
- package/src/sanityChecks/index.js +31 -0
- package/src/sanityChecks/isCreator.js +73 -0
- package/src/sanityChecks/needsRecoveryEmail.js +20 -0
- package/src/sanityChecks/rolloverNodes.js +74 -0
- package/src/sanityChecks/sanitizeNodes.js +24 -0
- package/src/sanityChecks/syncNodes.js +28 -0
- package/src/sdk/constants.ts +10 -0
- package/src/sdk/index.ts +1 -0
- package/src/sdk/oauth/Oauth.ts +265 -0
- package/src/sdk/oauth/index.ts +1 -0
- package/src/sdk/sdk.ts +102 -0
- package/src/service-selection/ServiceSelection.test.ts +320 -0
- package/src/service-selection/ServiceSelection.ts +460 -0
- package/src/service-selection/constants.ts +14 -0
- package/src/service-selection/index.ts +1 -0
- package/src/services/ABIDecoder/AudiusABIDecoder.ts +71 -0
- package/src/services/ABIDecoder/index.ts +1 -0
- package/src/services/comstock/Comstock.ts +39 -0
- package/src/services/comstock/index.ts +1 -0
- package/src/services/contracts/ContractClient.ts +227 -0
- package/src/services/contracts/GovernedContractClient.ts +53 -0
- package/src/services/contracts/ProviderSelection.ts +42 -0
- package/src/services/creatorNode/CreatorNode.ts +1065 -0
- package/src/services/creatorNode/CreatorNodeSelection.test.ts +997 -0
- package/src/services/creatorNode/CreatorNodeSelection.ts +488 -0
- package/src/services/creatorNode/constants.ts +10 -0
- package/src/services/creatorNode/index.ts +2 -0
- package/src/services/dataContracts/AudiusContracts.ts +234 -0
- package/src/services/dataContracts/IPLDBlacklistFactoryClient.ts +73 -0
- package/src/services/dataContracts/PlaylistFactoryClient.ts +370 -0
- package/src/services/dataContracts/RegistryClient.ts +95 -0
- package/src/services/dataContracts/SocialFeatureFactoryClient.ts +196 -0
- package/src/services/dataContracts/TrackFactoryClient.ts +131 -0
- package/src/services/dataContracts/UserFactoryClient.ts +351 -0
- package/src/services/dataContracts/UserLibraryFactoryClient.ts +115 -0
- package/src/services/dataContracts/UserReplicaSetManagerClient.ts +206 -0
- package/src/services/dataContracts/index.ts +1 -0
- package/src/services/discoveryProvider/DiscoveryProvider.ts +1168 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.test.ts +536 -0
- package/src/services/discoveryProvider/DiscoveryProviderSelection.ts +383 -0
- package/src/services/discoveryProvider/constants.ts +13 -0
- package/src/services/discoveryProvider/index.ts +1 -0
- package/src/services/discoveryProvider/requests.ts +629 -0
- package/src/services/ethContracts/AudiusTokenClient.ts +163 -0
- package/src/services/ethContracts/ClaimDistributionClient.ts +45 -0
- package/src/services/ethContracts/ClaimsManagerClient.ts +102 -0
- package/src/services/ethContracts/DelegateManagerClient.ts +480 -0
- package/src/services/ethContracts/EthContracts.ts +359 -0
- package/src/services/ethContracts/EthRewardsManagerClient.ts +33 -0
- package/src/services/ethContracts/GovernanceClient.ts +451 -0
- package/src/services/ethContracts/RegistryClient.ts +33 -0
- package/src/services/ethContracts/ServiceProviderFactoryClient.ts +691 -0
- package/src/services/ethContracts/ServiceTypeManagerClient.ts +112 -0
- package/src/services/ethContracts/StakingProxyClient.ts +97 -0
- package/src/services/ethContracts/TrustedNotifierManagerClient.ts +101 -0
- package/src/services/ethContracts/WormholeClient.ts +97 -0
- package/src/services/ethContracts/index.ts +1 -0
- package/src/services/ethWeb3Manager/EthWeb3Manager.ts +239 -0
- package/src/services/ethWeb3Manager/index.ts +1 -0
- package/src/services/hedgehog/Hedgehog.ts +96 -0
- package/src/services/hedgehog/index.ts +1 -0
- package/src/services/identity/IdentityService.ts +551 -0
- package/src/services/identity/index.ts +1 -0
- package/src/services/identity/requests.ts +65 -0
- package/src/services/schemaValidator/SchemaValidator.ts +105 -0
- package/src/services/schemaValidator/index.ts +1 -0
- package/src/services/schemaValidator/schemas/trackSchema.json +267 -0
- package/src/services/schemaValidator/schemas/userSchema.json +230 -0
- package/src/services/solanaAudiusData/errors.ts +20 -0
- package/src/services/solanaAudiusData/index.ts +1189 -0
- package/src/services/solanaWeb3Manager/errors.js +101 -0
- package/src/services/solanaWeb3Manager/index.d.ts +46 -0
- package/src/services/solanaWeb3Manager/index.js +655 -0
- package/src/services/solanaWeb3Manager/padBNToUint8Array.ts +7 -0
- package/src/services/solanaWeb3Manager/rewards.js +941 -0
- package/src/services/solanaWeb3Manager/rewardsAttester.ts +1093 -0
- package/src/services/solanaWeb3Manager/tokenAccount.js +149 -0
- package/src/services/solanaWeb3Manager/transactionHandler.js +345 -0
- package/src/services/solanaWeb3Manager/transfer.js +272 -0
- package/src/services/solanaWeb3Manager/userBank.js +160 -0
- package/src/services/solanaWeb3Manager/utils.d.ts +31 -0
- package/src/services/solanaWeb3Manager/utils.js +163 -0
- package/src/services/solanaWeb3Manager/wAudio.js +28 -0
- package/src/services/solanaWeb3Manager/wAudio.test.js +30 -0
- package/src/services/web3Manager/Web3Config.ts +14 -0
- package/src/services/web3Manager/Web3Manager.ts +360 -0
- package/src/services/web3Manager/XMLHttpRequest.ts +11 -0
- package/src/services/web3Manager/index.ts +2 -0
- package/src/services/wormhole/index.js +424 -0
- package/src/types.ts +8 -0
- package/src/userStateManager.ts +53 -0
- package/src/utils/apiSigning.ts +51 -0
- package/src/utils/captcha.ts +97 -0
- package/src/utils/estimateGas.ts +64 -0
- package/src/utils/fileHasher.ts +278 -0
- package/src/utils/importContractABI.d.ts +9 -0
- package/src/utils/importContractABI.js +19 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/multiProvider.ts +72 -0
- package/src/utils/network.test.ts +127 -0
- package/src/utils/network.ts +308 -0
- package/src/utils/promiseFight.test.ts +87 -0
- package/src/utils/promiseFight.ts +36 -0
- package/src/utils/signatures.ts +139 -0
- package/src/utils/types.ts +34 -0
- package/src/utils/utils.test.ts +36 -0
- package/src/utils/utils.ts +235 -0
- package/src/utils/uuid.ts +14 -0
- package/src/web3.d.ts +9 -0
- package/src/web3.js +8 -0
- package/tests/assets/static_image.png +0 -0
- package/tests/assets/static_text.txt +1 -0
- package/tests/audiusTokenClientTest.js +37 -0
- package/tests/creatorNodeTest.js +19 -0
- package/tests/fileHasherTest.js +125 -0
- package/tests/governanceTest.js +382 -0
- package/tests/helpers.js +105 -0
- package/tests/index.js +14 -0
- package/tests/playlistClientTest.js +157 -0
- package/tests/providerSelectionTest.js +241 -0
- package/tests/registryClientTest.js +19 -0
- package/tests/rewardsAttesterTest.js +373 -0
- package/tests/serviceTypeManagerClientTest.js +33 -0
- package/tests/socialFeatureClientTest.js +79 -0
- package/tests/stakingTest.js +302 -0
- package/tests/trackClientTest.js +86 -0
- package/tests/userClientTest.js +121 -0
- package/tsconfig.json +10 -0
- package/types/@audius-hedgehog/index.d.ts +39 -0
- package/types/abi-decoder/index.d.ts +41 -0
|
@@ -0,0 +1,1168 @@
|
|
|
1
|
+
// TODO: strictly type each method with the models defined in audius-client
|
|
2
|
+
import axios, { AxiosError, AxiosRequestConfig, Method } from 'axios'
|
|
3
|
+
|
|
4
|
+
import { Utils } from '../../utils'
|
|
5
|
+
|
|
6
|
+
import { DEFAULT_UNHEALTHY_BLOCK_DIFF, REQUEST_TIMEOUT_MS } from './constants'
|
|
7
|
+
|
|
8
|
+
import * as Requests from './requests'
|
|
9
|
+
|
|
10
|
+
import urlJoin, { PathArg } from 'proper-url-join'
|
|
11
|
+
import {
|
|
12
|
+
DiscoveryProviderSelection,
|
|
13
|
+
DiscoveryProviderSelectionConfig
|
|
14
|
+
} from './DiscoveryProviderSelection'
|
|
15
|
+
import type { CurrentUser, UserStateManager } from '../../userStateManager'
|
|
16
|
+
import type { EthContracts } from '../ethContracts'
|
|
17
|
+
import type { Web3Manager } from '../web3Manager'
|
|
18
|
+
|
|
19
|
+
const MAX_MAKE_REQUEST_RETRY_COUNT = 5
|
|
20
|
+
const MAX_MAKE_REQUEST_RETRIES_WITH_404 = 2
|
|
21
|
+
|
|
22
|
+
type RequestParams = {
|
|
23
|
+
queryParams: Record<string, string>
|
|
24
|
+
endpoint: string
|
|
25
|
+
timeout?: number
|
|
26
|
+
method?: Method
|
|
27
|
+
urlParams?: PathArg
|
|
28
|
+
headers?: Record<string, string>
|
|
29
|
+
data?: Record<string, unknown>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type DiscoveryProviderConfig = {
|
|
33
|
+
whitelist?: Set<string>
|
|
34
|
+
blacklist?: Set<string>
|
|
35
|
+
userStateManager: UserStateManager
|
|
36
|
+
ethContracts: EthContracts
|
|
37
|
+
web3Manager?: Web3Manager
|
|
38
|
+
reselectTimeout?: number
|
|
39
|
+
selectionCallback?: DiscoveryProviderSelectionConfig['selectionCallback']
|
|
40
|
+
monitoringCallbacks?: DiscoveryProviderSelectionConfig['monitoringCallbacks']
|
|
41
|
+
selectionRequestTimeout?: number
|
|
42
|
+
selectionRequestRetries?: number
|
|
43
|
+
unhealthySlotDiffPlays?: number
|
|
44
|
+
unhealthyBlockDiff?: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type UserProfile = {
|
|
48
|
+
userId: number
|
|
49
|
+
email: string
|
|
50
|
+
name: string
|
|
51
|
+
handle: string
|
|
52
|
+
verified: boolean
|
|
53
|
+
imageURL?: string
|
|
54
|
+
sub: number
|
|
55
|
+
iat: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Constructs a service class for a discovery node
|
|
60
|
+
* @param whitelist whether or not to only include specified nodes in selection
|
|
61
|
+
* @param userStateManager singleton UserStateManager instance
|
|
62
|
+
* @param ethContracts singleton EthContracts instance
|
|
63
|
+
* @param web3Manager
|
|
64
|
+
* @param reselectTimeout timeout to clear locally cached discovery providers
|
|
65
|
+
* @param selectionCallback invoked when a discovery node is selected
|
|
66
|
+
* @param monitoringCallbacks callbacks to be invoked with metrics from requests sent to a service
|
|
67
|
+
* @param monitoringCallbacks.request
|
|
68
|
+
* @param monitoringCallbacks.healthCheck
|
|
69
|
+
* @param selectionRequestTimeout the amount of time (ms) an individual request should take before reselecting
|
|
70
|
+
* @param selectionRequestRetries the number of retries to a given discovery node we make before reselecting
|
|
71
|
+
* @param unhealthySlotDiffPlays the number of slots we would consider a discovery node unhealthy
|
|
72
|
+
* @param unhealthyBlockDiff the number of missed blocks after which we would consider a discovery node unhealthy
|
|
73
|
+
*/
|
|
74
|
+
export class DiscoveryProvider {
|
|
75
|
+
whitelist: Set<string> | undefined
|
|
76
|
+
blacklist: Set<string> | undefined
|
|
77
|
+
userStateManager: UserStateManager
|
|
78
|
+
ethContracts: EthContracts
|
|
79
|
+
web3Manager: Web3Manager | undefined
|
|
80
|
+
unhealthyBlockDiff: number
|
|
81
|
+
serviceSelector: DiscoveryProviderSelection
|
|
82
|
+
selectionRequestTimeout: number
|
|
83
|
+
selectionRequestRetries: number
|
|
84
|
+
unhealthySlotDiffPlays: number | undefined
|
|
85
|
+
request404Count: number
|
|
86
|
+
maxRequestsForTrue404: number
|
|
87
|
+
monitoringCallbacks:
|
|
88
|
+
| DiscoveryProviderSelection['monitoringCallbacks']
|
|
89
|
+
| undefined
|
|
90
|
+
|
|
91
|
+
discoveryProviderEndpoint: string | undefined
|
|
92
|
+
|
|
93
|
+
constructor({
|
|
94
|
+
whitelist,
|
|
95
|
+
blacklist,
|
|
96
|
+
userStateManager,
|
|
97
|
+
ethContracts,
|
|
98
|
+
web3Manager,
|
|
99
|
+
reselectTimeout,
|
|
100
|
+
selectionCallback,
|
|
101
|
+
monitoringCallbacks,
|
|
102
|
+
selectionRequestTimeout = REQUEST_TIMEOUT_MS,
|
|
103
|
+
selectionRequestRetries = MAX_MAKE_REQUEST_RETRY_COUNT,
|
|
104
|
+
unhealthySlotDiffPlays,
|
|
105
|
+
unhealthyBlockDiff
|
|
106
|
+
}: DiscoveryProviderConfig) {
|
|
107
|
+
this.whitelist = whitelist
|
|
108
|
+
this.blacklist = blacklist
|
|
109
|
+
this.userStateManager = userStateManager
|
|
110
|
+
this.ethContracts = ethContracts
|
|
111
|
+
this.web3Manager = web3Manager
|
|
112
|
+
|
|
113
|
+
this.unhealthyBlockDiff = unhealthyBlockDiff ?? DEFAULT_UNHEALTHY_BLOCK_DIFF
|
|
114
|
+
this.serviceSelector = new DiscoveryProviderSelection(
|
|
115
|
+
{
|
|
116
|
+
whitelist: this.whitelist,
|
|
117
|
+
blacklist: this.blacklist,
|
|
118
|
+
reselectTimeout,
|
|
119
|
+
selectionCallback,
|
|
120
|
+
monitoringCallbacks,
|
|
121
|
+
requestTimeout: selectionRequestTimeout,
|
|
122
|
+
unhealthySlotDiffPlays: unhealthySlotDiffPlays,
|
|
123
|
+
unhealthyBlockDiff: this.unhealthyBlockDiff
|
|
124
|
+
},
|
|
125
|
+
this.ethContracts
|
|
126
|
+
)
|
|
127
|
+
this.selectionRequestTimeout = selectionRequestTimeout
|
|
128
|
+
this.selectionRequestRetries = selectionRequestRetries
|
|
129
|
+
this.unhealthySlotDiffPlays = unhealthySlotDiffPlays
|
|
130
|
+
|
|
131
|
+
// Keep track of the number of times a request 404s so we know when a true 404 occurs
|
|
132
|
+
// Due to incident where some discovery nodes may erroneously be missing content #flare-51,
|
|
133
|
+
// we treat 404s differently than generic 4xx's or other 5xx errors.
|
|
134
|
+
// In the case of a 404, try a few other nodes
|
|
135
|
+
this.request404Count = 0
|
|
136
|
+
this.maxRequestsForTrue404 = MAX_MAKE_REQUEST_RETRIES_WITH_404
|
|
137
|
+
|
|
138
|
+
this.monitoringCallbacks = monitoringCallbacks
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async init() {
|
|
142
|
+
const endpoint = await this.serviceSelector.select()
|
|
143
|
+
this.setEndpoint(endpoint)
|
|
144
|
+
|
|
145
|
+
if (endpoint && this.web3Manager && this.web3Manager.web3) {
|
|
146
|
+
// Set current user if it exists
|
|
147
|
+
const userAccount = await this.getUserAccount(
|
|
148
|
+
this.web3Manager.getWalletAddress()
|
|
149
|
+
)
|
|
150
|
+
if (userAccount) this.userStateManager.setCurrentUser(userAccount)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
setEndpoint(endpoint: string) {
|
|
155
|
+
this.discoveryProviderEndpoint = endpoint
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setUnhealthyBlockDiff(updatedBlockDiff = DEFAULT_UNHEALTHY_BLOCK_DIFF) {
|
|
159
|
+
this.unhealthyBlockDiff = updatedBlockDiff
|
|
160
|
+
this.serviceSelector.setUnhealthyBlockDiff(updatedBlockDiff)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setUnhealthySlotDiffPlays(updatedDiff: number) {
|
|
164
|
+
this.unhealthySlotDiffPlays = updatedDiff
|
|
165
|
+
this.serviceSelector.setUnhealthySlotDiffPlays(updatedDiff)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get users with all relevant user data
|
|
170
|
+
* can be filtered by providing an integer array of ids
|
|
171
|
+
* @param limit
|
|
172
|
+
* @param offset
|
|
173
|
+
* @param idsArray
|
|
174
|
+
* @param walletAddress
|
|
175
|
+
* @param handle
|
|
176
|
+
* @param isCreator null returns all users, true returns creators only, false returns users only
|
|
177
|
+
* @returns {Object} {Array of User metadata Objects}
|
|
178
|
+
* additional metadata fields on user objects:
|
|
179
|
+
* {Integer} track_count - track count for given user
|
|
180
|
+
* {Integer} playlist_count - playlist count for given user
|
|
181
|
+
* {Integer} album_count - album count for given user
|
|
182
|
+
* {Integer} follower_count - follower count for given user
|
|
183
|
+
* {Integer} followee_count - followee count for given user
|
|
184
|
+
* {Integer} repost_count - repost count for given user
|
|
185
|
+
* {Integer} track_blocknumber - blocknumber of latest track for user
|
|
186
|
+
* {Boolean} does_current_user_follow - does current user follow given user
|
|
187
|
+
* {Array} followee_follows - followees of current user that follow given user
|
|
188
|
+
* @example
|
|
189
|
+
* await getUsers()
|
|
190
|
+
* await getUsers(100, 0, [3,2,6]) - Invalid user ids will not be accepted
|
|
191
|
+
*/
|
|
192
|
+
async getUsers(
|
|
193
|
+
limit = 100,
|
|
194
|
+
offset = 0,
|
|
195
|
+
idsArray?: string[],
|
|
196
|
+
walletAddress?: string,
|
|
197
|
+
handle?: string,
|
|
198
|
+
isCreator = null,
|
|
199
|
+
minBlockNumber?: number
|
|
200
|
+
) {
|
|
201
|
+
const req = Requests.getUsers(
|
|
202
|
+
limit,
|
|
203
|
+
offset,
|
|
204
|
+
idsArray,
|
|
205
|
+
walletAddress,
|
|
206
|
+
handle,
|
|
207
|
+
isCreator,
|
|
208
|
+
minBlockNumber
|
|
209
|
+
)
|
|
210
|
+
return await this._makeRequest(req)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* get tracks with all relevant track data
|
|
215
|
+
* can be filtered by providing an integer array of ids
|
|
216
|
+
* @param limit
|
|
217
|
+
* @param offset
|
|
218
|
+
* @param idsArray
|
|
219
|
+
* @param targetUserId the owner of the tracks being queried
|
|
220
|
+
* @param sort a string of form eg. blocknumber:asc,timestamp:desc describing a sort path
|
|
221
|
+
* @param minBlockNumber The min block number
|
|
222
|
+
* @param filterDeleted If set to true, filters the deleted tracks
|
|
223
|
+
* @returns {Object} {Array of track metadata Objects}
|
|
224
|
+
* additional metadata fields on track objects:
|
|
225
|
+
* {Integer} repost_count - repost count for given track
|
|
226
|
+
* {Integer} save_count - save count for given track
|
|
227
|
+
* {Array} followee_reposts - followees of current user that have reposted given track
|
|
228
|
+
* {Boolean} has_current_user_reposted - has current user reposted given track
|
|
229
|
+
* {Boolean} has_current_user_saved - has current user saved given track
|
|
230
|
+
* @example
|
|
231
|
+
* await getTracks()
|
|
232
|
+
* await getTracks(100, 0, [3,2,6]) - Invalid track ids will not be accepted
|
|
233
|
+
*/
|
|
234
|
+
async getTracks(
|
|
235
|
+
limit = 100,
|
|
236
|
+
offset = 0,
|
|
237
|
+
idsArray?: string[],
|
|
238
|
+
targetUserId?: string,
|
|
239
|
+
sort?: boolean,
|
|
240
|
+
minBlockNumber?: number,
|
|
241
|
+
filterDeleted?: boolean,
|
|
242
|
+
withUsers?: boolean
|
|
243
|
+
) {
|
|
244
|
+
const req = Requests.getTracks(
|
|
245
|
+
limit,
|
|
246
|
+
offset,
|
|
247
|
+
idsArray,
|
|
248
|
+
targetUserId,
|
|
249
|
+
sort,
|
|
250
|
+
minBlockNumber,
|
|
251
|
+
filterDeleted,
|
|
252
|
+
withUsers
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return await this._makeRequest(req)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Gets a particular track by its creator's handle and the track's URL slug
|
|
260
|
+
* @param handle the handle of the owner of the track
|
|
261
|
+
* @param slug the URL slug of the track, generally the title urlized
|
|
262
|
+
* @returns {Object} the requested track's metadata
|
|
263
|
+
*/
|
|
264
|
+
async getTracksByHandleAndSlug(handle: string, slug: string) {
|
|
265
|
+
// Note: retries are disabled here because the v1 API response returns a 404 instead
|
|
266
|
+
// of an empty array, which can cause a retry storm.
|
|
267
|
+
// TODO: Rewrite this API with something more effective, change makeRequest to
|
|
268
|
+
// support 404s and not retry & use AudiusAPIClient.
|
|
269
|
+
return await this._makeRequest(
|
|
270
|
+
Requests.getTracksByHandleAndSlug(handle, slug),
|
|
271
|
+
/* retry */ false
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {Object} getTracksIdentifier
|
|
277
|
+
* @property {string} handle
|
|
278
|
+
* @property {number} id
|
|
279
|
+
* @property {string} url_title
|
|
280
|
+
*/
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* gets all tracks matching identifiers, including unlisted.
|
|
284
|
+
*
|
|
285
|
+
* @param identifiers
|
|
286
|
+
* @returns {(Array)} track
|
|
287
|
+
*/
|
|
288
|
+
async getTracksIncludingUnlisted(identifiers: string[], withUsers = false) {
|
|
289
|
+
const req = Requests.getTracksIncludingUnlisted(identifiers, withUsers)
|
|
290
|
+
return await this._makeRequest(req)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Gets random tracks from trending tracks for a given genre.
|
|
295
|
+
* If genre not given, will return trending tracks across all genres.
|
|
296
|
+
* Excludes specified track ids.
|
|
297
|
+
*
|
|
298
|
+
* @param genre
|
|
299
|
+
* @param limit
|
|
300
|
+
* @param exclusionList
|
|
301
|
+
* @param time
|
|
302
|
+
* @returns {(Array)} track
|
|
303
|
+
*/
|
|
304
|
+
async getRandomTracks(
|
|
305
|
+
genre: string,
|
|
306
|
+
limit: number,
|
|
307
|
+
exclusionList: number[],
|
|
308
|
+
time: string
|
|
309
|
+
) {
|
|
310
|
+
const req = Requests.getRandomTracks(genre, limit, exclusionList, time)
|
|
311
|
+
return await this._makeRequest(req)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Gets all stems for a given trackId as an array of tracks.
|
|
316
|
+
* @param trackId
|
|
317
|
+
* @returns {(Array)} track
|
|
318
|
+
*/
|
|
319
|
+
async getStemsForTrack(trackId: number) {
|
|
320
|
+
const req = Requests.getStemsForTrack(trackId)
|
|
321
|
+
return await this._makeRequest(req)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Gets all the remixes of a given trackId as an array of tracks.
|
|
326
|
+
* @param trackId
|
|
327
|
+
* @param limit
|
|
328
|
+
* @param offset
|
|
329
|
+
* @returns {(Array)} track
|
|
330
|
+
*/
|
|
331
|
+
async getRemixesOfTrack(trackId: number, limit?: number, offset?: number) {
|
|
332
|
+
const req = Requests.getRemixesOfTrack(trackId, limit, offset)
|
|
333
|
+
return await this._makeRequest(req)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Gets the remix parents of a given trackId as an array of tracks.
|
|
338
|
+
* @param limit
|
|
339
|
+
* @param offset
|
|
340
|
+
* @returns {(Array)} track
|
|
341
|
+
*/
|
|
342
|
+
async getRemixTrackParents(trackId: number, limit?: number, offset?: number) {
|
|
343
|
+
const req = Requests.getRemixTrackParents(trackId, limit, offset)
|
|
344
|
+
return await this._makeRequest(req)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Gets tracks trending on Audius.
|
|
349
|
+
* @param genre
|
|
350
|
+
* @param timeFrame one of day, week, month, or year
|
|
351
|
+
* @param idsArray track ids
|
|
352
|
+
* @param limit
|
|
353
|
+
* @param offset
|
|
354
|
+
*/
|
|
355
|
+
async getTrendingTracks(
|
|
356
|
+
genre?: string,
|
|
357
|
+
timeFrame?: string,
|
|
358
|
+
idsArray?: number[],
|
|
359
|
+
limit?: number,
|
|
360
|
+
offset?: number,
|
|
361
|
+
withUsers = false
|
|
362
|
+
) {
|
|
363
|
+
const req = Requests.getTrendingTracks(
|
|
364
|
+
genre,
|
|
365
|
+
timeFrame,
|
|
366
|
+
idsArray,
|
|
367
|
+
limit,
|
|
368
|
+
offset,
|
|
369
|
+
withUsers
|
|
370
|
+
)
|
|
371
|
+
return await this._makeRequest<{
|
|
372
|
+
listenCounts: Array<{ trackId: number; listens: number }>
|
|
373
|
+
}>(req)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* get full playlist objects, including tracks, for passed in array of playlistId
|
|
378
|
+
* @returns {Array} array of playlist objects
|
|
379
|
+
* additional metadata fields on playlist objects:
|
|
380
|
+
* {Integer} repost_count - repost count for given playlist
|
|
381
|
+
* {Integer} save_count - save count for given playlist
|
|
382
|
+
* {Boolean} has_current_user_reposted - has current user reposted given playlist
|
|
383
|
+
* {Array} followee_reposts - followees of current user that have reposted given playlist
|
|
384
|
+
* {Boolean} has_current_user_reposted - has current user reposted given playlist
|
|
385
|
+
* {Boolean} has_current_user_saved - has current user saved given playlist
|
|
386
|
+
*/
|
|
387
|
+
async getPlaylists(
|
|
388
|
+
limit = 100,
|
|
389
|
+
offset = 0,
|
|
390
|
+
idsArray = null,
|
|
391
|
+
targetUserId = null,
|
|
392
|
+
withUsers = false
|
|
393
|
+
) {
|
|
394
|
+
const req = Requests.getPlaylists(
|
|
395
|
+
limit,
|
|
396
|
+
offset,
|
|
397
|
+
idsArray,
|
|
398
|
+
targetUserId,
|
|
399
|
+
withUsers
|
|
400
|
+
)
|
|
401
|
+
return await this._makeRequest(req)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Return social feed for current user
|
|
406
|
+
* @param filter - filter by "all", "original", or "repost"
|
|
407
|
+
* @param limit - max # of items to return
|
|
408
|
+
* @param offset - offset into list to return from (for pagination)
|
|
409
|
+
* @returns {Object} {Array of track and playlist metadata objects}
|
|
410
|
+
* additional metadata fields on track and playlist objects:
|
|
411
|
+
* {String} activity_timestamp - timestamp of requested user's repost for given track or playlist,
|
|
412
|
+
* used for sorting feed
|
|
413
|
+
* {Integer} repost_count - repost count of given track/playlist
|
|
414
|
+
* {Integer} save_count - save count of given track/playlist
|
|
415
|
+
* {Boolean} has_current_user_reposted - has current user reposted given track/playlist
|
|
416
|
+
* {Array} followee_reposts - followees of current user that have reposted given track/playlist
|
|
417
|
+
*/
|
|
418
|
+
async getSocialFeed(
|
|
419
|
+
filter: string,
|
|
420
|
+
limit = 100,
|
|
421
|
+
offset = 0,
|
|
422
|
+
withUsers = false,
|
|
423
|
+
tracksOnly = false
|
|
424
|
+
) {
|
|
425
|
+
const req = Requests.getSocialFeed(
|
|
426
|
+
filter,
|
|
427
|
+
limit,
|
|
428
|
+
offset,
|
|
429
|
+
withUsers,
|
|
430
|
+
tracksOnly
|
|
431
|
+
)
|
|
432
|
+
return await this._makeRequest(req)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Return repost feed for requested user
|
|
437
|
+
* @param userId - requested user id
|
|
438
|
+
* @param limit - max # of items to return (for pagination)
|
|
439
|
+
* @param offset - offset into list to return from (for pagination)
|
|
440
|
+
* @returns {Object} {Array of track and playlist metadata objects}
|
|
441
|
+
* additional metadata fields on track and playlist objects:
|
|
442
|
+
* {String} activity_timestamp - timestamp of requested user's repost for given track or playlist,
|
|
443
|
+
* used for sorting feed
|
|
444
|
+
* {Integer} repost_count - repost count of given track/playlist
|
|
445
|
+
* {Integer} save_count - save count of given track/playlist
|
|
446
|
+
* {Boolean} has_current_user_reposted - has current user reposted given track/playlist
|
|
447
|
+
* {Array} followee_reposts - followees of current user that have reposted given track/playlist
|
|
448
|
+
*/
|
|
449
|
+
async getUserRepostFeed(
|
|
450
|
+
userId: number,
|
|
451
|
+
limit = 100,
|
|
452
|
+
offset = 0,
|
|
453
|
+
withUsers = false
|
|
454
|
+
) {
|
|
455
|
+
const req = Requests.getUserRepostFeed(userId, limit, offset, withUsers)
|
|
456
|
+
return await this._makeRequest(req)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* get intersection of users that follow followeeUserId and users that are followed by followerUserId
|
|
461
|
+
* @param followeeUserId user that is followed
|
|
462
|
+
* @param followerUserId user that follows
|
|
463
|
+
* @example
|
|
464
|
+
* getFollowIntersectionUsers(100, 0, 1, 1) - IDs must be valid
|
|
465
|
+
*/
|
|
466
|
+
async getFollowIntersectionUsers(
|
|
467
|
+
limit = 100,
|
|
468
|
+
offset = 0,
|
|
469
|
+
followeeUserId: number,
|
|
470
|
+
followerUserId: number
|
|
471
|
+
) {
|
|
472
|
+
const req = Requests.getFollowIntersectionUsers(
|
|
473
|
+
limit,
|
|
474
|
+
offset,
|
|
475
|
+
followeeUserId,
|
|
476
|
+
followerUserId
|
|
477
|
+
)
|
|
478
|
+
return await this._makeRequest(req)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* get intersection of users that have reposted repostTrackId and users that are followed by followerUserId
|
|
483
|
+
* followee = user that is followed; follower = user that follows
|
|
484
|
+
* @param repostTrackId track that is reposted
|
|
485
|
+
* @param followerUserId user that reposted track
|
|
486
|
+
* @example
|
|
487
|
+
* getTrackRepostIntersectionUsers(100, 0, 1, 1) - IDs must be valid
|
|
488
|
+
*/
|
|
489
|
+
async getTrackRepostIntersectionUsers(
|
|
490
|
+
limit = 100,
|
|
491
|
+
offset = 0,
|
|
492
|
+
repostTrackId: number,
|
|
493
|
+
followerUserId: number
|
|
494
|
+
) {
|
|
495
|
+
const req = Requests.getTrackRepostIntersectionUsers(
|
|
496
|
+
limit,
|
|
497
|
+
offset,
|
|
498
|
+
repostTrackId,
|
|
499
|
+
followerUserId
|
|
500
|
+
)
|
|
501
|
+
return await this._makeRequest(req)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* get intersection of users that have reposted repostPlaylistId and users that are followed by followerUserId
|
|
506
|
+
* followee = user that is followed; follower = user that follows
|
|
507
|
+
* @param repostPlaylistId playlist that is reposted
|
|
508
|
+
* @param followerUserId user that reposted track
|
|
509
|
+
* @example
|
|
510
|
+
* getPlaylistRepostIntersectionUsers(100, 0, 1, 1) - IDs must be valid
|
|
511
|
+
*/
|
|
512
|
+
async getPlaylistRepostIntersectionUsers(
|
|
513
|
+
limit = 100,
|
|
514
|
+
offset = 0,
|
|
515
|
+
repostPlaylistId: number,
|
|
516
|
+
followerUserId: number
|
|
517
|
+
) {
|
|
518
|
+
const req = Requests.getPlaylistRepostIntersectionUsers(
|
|
519
|
+
limit,
|
|
520
|
+
offset,
|
|
521
|
+
repostPlaylistId,
|
|
522
|
+
followerUserId
|
|
523
|
+
)
|
|
524
|
+
return await this._makeRequest(req)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* get users that follow followeeUserId, sorted by follower count descending
|
|
529
|
+
* @param followeeUserId user that is followed
|
|
530
|
+
* @return {Array} array of user objects with standard user metadata
|
|
531
|
+
*/
|
|
532
|
+
async getFollowersForUser(limit = 100, offset = 0, followeeUserId: number) {
|
|
533
|
+
const req = Requests.getFollowersForUser(limit, offset, followeeUserId)
|
|
534
|
+
return await this._makeRequest(req)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* get users that are followed by followerUserId, sorted by follower count descending
|
|
539
|
+
* @param followerUserId user - i am the one who follows
|
|
540
|
+
* @return {Array} array of user objects with standard user metadata
|
|
541
|
+
*/
|
|
542
|
+
async getFolloweesForUser(limit = 100, offset = 0, followerUserId: number) {
|
|
543
|
+
const req = Requests.getFolloweesForUser(limit, offset, followerUserId)
|
|
544
|
+
return await this._makeRequest(req)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* get users that reposted repostTrackId, sorted by follower count descending
|
|
549
|
+
* @param repostTrackId
|
|
550
|
+
* @return {Array} array of user objects
|
|
551
|
+
* additional metadata fields on user objects:
|
|
552
|
+
* {Integer} follower_count - follower count of given user
|
|
553
|
+
* @example
|
|
554
|
+
* getRepostersForTrack(100, 0, 1) - ID must be valid
|
|
555
|
+
*/
|
|
556
|
+
async getRepostersForTrack(limit = 100, offset = 0, repostTrackId: number) {
|
|
557
|
+
const req = Requests.getRepostersForTrack(limit, offset, repostTrackId)
|
|
558
|
+
return await this._makeRequest(req)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* get users that reposted repostPlaylistId, sorted by follower count descending
|
|
563
|
+
* @param repostPlaylistId
|
|
564
|
+
* @return {Array} array of user objects
|
|
565
|
+
* additional metadata fields on user objects:
|
|
566
|
+
* {Integer} follower_count - follower count of given user
|
|
567
|
+
* @example
|
|
568
|
+
* getRepostersForPlaylist(100, 0, 1) - ID must be valid
|
|
569
|
+
*/
|
|
570
|
+
async getRepostersForPlaylist(
|
|
571
|
+
limit = 100,
|
|
572
|
+
offset = 0,
|
|
573
|
+
repostPlaylistId: number
|
|
574
|
+
) {
|
|
575
|
+
const req = Requests.getRepostersForPlaylist(
|
|
576
|
+
limit,
|
|
577
|
+
offset,
|
|
578
|
+
repostPlaylistId
|
|
579
|
+
)
|
|
580
|
+
return await this._makeRequest(req)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* get users that saved saveTrackId, sorted by follower count descending
|
|
585
|
+
* @param saveTrackId
|
|
586
|
+
* @return {Array} array of user objects
|
|
587
|
+
* additional metadata fields on user objects:
|
|
588
|
+
* {Integer} follower_count - follower count of given user
|
|
589
|
+
* @example
|
|
590
|
+
* getSaversForTrack(100, 0, 1) - ID must be valid
|
|
591
|
+
*/
|
|
592
|
+
async getSaversForTrack(limit = 100, offset = 0, saveTrackId: number) {
|
|
593
|
+
const req = Requests.getSaversForTrack(limit, offset, saveTrackId)
|
|
594
|
+
return await this._makeRequest(req)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* get users that saved savePlaylistId, sorted by follower count descending
|
|
599
|
+
* @param savePlaylistId
|
|
600
|
+
* @return {Array} array of user objects
|
|
601
|
+
* additional metadata fields on user objects:
|
|
602
|
+
* {Integer} follower_count - follower count of given user
|
|
603
|
+
* @example
|
|
604
|
+
* getSaversForPlaylist(100, 0, 1) - ID must be valid
|
|
605
|
+
*/
|
|
606
|
+
async getSaversForPlaylist(limit = 100, offset = 0, savePlaylistId: number) {
|
|
607
|
+
const req = Requests.getSaversForPlaylist(limit, offset, savePlaylistId)
|
|
608
|
+
return await this._makeRequest(req)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* get whether a JWT given by Audius Oauth popup is valid
|
|
613
|
+
* @param token - JWT
|
|
614
|
+
* @return {UserProfile | false} profile info of user attached to JWT payload if the JWT is valid, else false
|
|
615
|
+
*/
|
|
616
|
+
async verifyToken(token: string): Promise<UserProfile | false> {
|
|
617
|
+
const req = Requests.verifyToken(token)
|
|
618
|
+
const res = await this._makeRequest<UserProfile[]>(req)
|
|
619
|
+
if (res == null || res[0] == null) {
|
|
620
|
+
return false
|
|
621
|
+
} else {
|
|
622
|
+
return res[0]
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Perform a full-text search. Returns tracks, users, playlists, albums
|
|
628
|
+
* with optional user-specific results for each
|
|
629
|
+
* - user, track, and playlist objects have all same data as returned from standalone endpoints
|
|
630
|
+
* @param text search query
|
|
631
|
+
* @param kind 'tracks', 'users', 'playlists', 'albums', 'all'
|
|
632
|
+
* @param limit max # of items to return per list (for pagination)
|
|
633
|
+
* @param offset offset into list to return from (for pagination)
|
|
634
|
+
*/
|
|
635
|
+
async searchFull(text: string, kind: string, limit = 100, offset = 0) {
|
|
636
|
+
const req = Requests.searchFull(text, kind, limit, offset)
|
|
637
|
+
return await this._makeRequest(req)
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Perform a lighter-weight full-text search. Returns tracks, users, playlists, albums
|
|
642
|
+
* with optional user-specific results for each
|
|
643
|
+
* - user, track, and playlist objects have core data, and track & playlist objects
|
|
644
|
+
* also return user object
|
|
645
|
+
* @param text search query
|
|
646
|
+
* @param limit max # of items to return per list (for pagination)
|
|
647
|
+
* @param offset offset into list to return from (for pagination)
|
|
648
|
+
*/
|
|
649
|
+
async searchAutocomplete(text: string, limit = 100, offset = 0) {
|
|
650
|
+
const req = Requests.searchAutocomplete(text, limit, offset)
|
|
651
|
+
return await this._makeRequest(req)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Perform a tags-only search. Returns tracks with required tag and users
|
|
656
|
+
* that have used a tag greater than a specified number of times
|
|
657
|
+
* @param text search query
|
|
658
|
+
* @param userTagCount min # of times a user must have used a tag to be returned
|
|
659
|
+
* @param kind 'tracks', 'users', 'playlists', 'albums', 'all'
|
|
660
|
+
* @param limit max # of items to return per list (for pagination)
|
|
661
|
+
* @param offset offset into list to return from (for pagination)
|
|
662
|
+
*/
|
|
663
|
+
async searchTags(
|
|
664
|
+
text: string,
|
|
665
|
+
userTagCount = 2,
|
|
666
|
+
kind = 'all',
|
|
667
|
+
limit = 100,
|
|
668
|
+
offset = 0
|
|
669
|
+
) {
|
|
670
|
+
const req = Requests.searchTags(text, userTagCount, kind, limit, offset)
|
|
671
|
+
return await this._makeRequest(req)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Return saved playlists for current user
|
|
676
|
+
* NOTE in returned JSON, SaveType string one of track, playlist, album
|
|
677
|
+
* @param limit - max # of items to return
|
|
678
|
+
* @param offset - offset into list to return from (for pagination)
|
|
679
|
+
*/
|
|
680
|
+
async getSavedPlaylists(limit = 100, offset = 0, withUsers = false) {
|
|
681
|
+
const req = Requests.getSavedPlaylists(limit, offset, withUsers)
|
|
682
|
+
return await this._makeRequest(req)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Return saved albums for current user
|
|
687
|
+
* NOTE in returned JSON, SaveType string one of track, playlist, album
|
|
688
|
+
* @param limit - max # of items to return
|
|
689
|
+
* @param offset - offset into list to return from (for pagination)
|
|
690
|
+
*/
|
|
691
|
+
async getSavedAlbums(limit = 100, offset = 0, withUsers = false) {
|
|
692
|
+
const req = Requests.getSavedAlbums(limit, offset, withUsers)
|
|
693
|
+
return await this._makeRequest(req)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Return saved tracks for current user
|
|
698
|
+
* NOTE in returned JSON, SaveType string one of track, playlist, album
|
|
699
|
+
* @param limit - max # of items to return
|
|
700
|
+
* @param offset - offset into list to return from (for pagination)
|
|
701
|
+
*/
|
|
702
|
+
async getSavedTracks(limit = 100, offset = 0, withUsers = false) {
|
|
703
|
+
const req = Requests.getSavedTracks(limit, offset, withUsers)
|
|
704
|
+
return await this._makeRequest(req)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Return user collections (saved & uploaded) along w/ users for those collections
|
|
709
|
+
*/
|
|
710
|
+
async getUserAccount(wallet: string) {
|
|
711
|
+
const req = Requests.getUserAccount(wallet)
|
|
712
|
+
return await this._makeRequest<CurrentUser>(req)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async getTopPlaylists(
|
|
716
|
+
type: string,
|
|
717
|
+
limit: number,
|
|
718
|
+
mood: string,
|
|
719
|
+
filter: string,
|
|
720
|
+
withUsers = false
|
|
721
|
+
) {
|
|
722
|
+
const req = Requests.getTopPlaylists(type, limit, mood, filter, withUsers)
|
|
723
|
+
return await this._makeRequest(req)
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
async getTopFolloweeWindowed(
|
|
727
|
+
type: string,
|
|
728
|
+
window: string,
|
|
729
|
+
limit: string,
|
|
730
|
+
withUsers = false
|
|
731
|
+
) {
|
|
732
|
+
const req = Requests.getTopFolloweeWindowed(type, window, limit, withUsers)
|
|
733
|
+
return await this._makeRequest(req)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async getTopFolloweeSaves(type: string, limit: string, withUsers = false) {
|
|
737
|
+
const req = Requests.getTopFolloweeSaves(type, limit, withUsers)
|
|
738
|
+
return await this._makeRequest(req)
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async getLatest(type: string) {
|
|
742
|
+
const req = Requests.getLatest(type)
|
|
743
|
+
return await this._makeRequest(req)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
async getTopCreatorsByGenres(
|
|
747
|
+
genres: string[],
|
|
748
|
+
limit = 30,
|
|
749
|
+
offset = 0,
|
|
750
|
+
withUsers = false
|
|
751
|
+
) {
|
|
752
|
+
const req = Requests.getTopCreatorsByGenres(
|
|
753
|
+
genres,
|
|
754
|
+
limit,
|
|
755
|
+
offset,
|
|
756
|
+
withUsers
|
|
757
|
+
)
|
|
758
|
+
return await this._makeRequest(req)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
async getURSMContentNodes(ownerWallet: string | null = null) {
|
|
762
|
+
const req = Requests.getURSMContentNodes(ownerWallet)
|
|
763
|
+
return await this._makeRequest(req)
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
async getNotifications(
|
|
767
|
+
minBlockNumber: string,
|
|
768
|
+
trackIds: string[],
|
|
769
|
+
timeout: number
|
|
770
|
+
) {
|
|
771
|
+
const req = Requests.getNotifications(minBlockNumber, trackIds, timeout)
|
|
772
|
+
return await this._makeRequest(req)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async getSolanaNotifications(minSlotNumber: number, timeout: number) {
|
|
776
|
+
const req = Requests.getSolanaNotifications(minSlotNumber, timeout)
|
|
777
|
+
return await this._makeRequest(req)
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async getTrackListenMilestones(timeout: number) {
|
|
781
|
+
const req = Requests.getTrackListenMilestones(timeout)
|
|
782
|
+
return await this._makeRequest(req)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async getChallengeAttestation(
|
|
786
|
+
challengeId: string,
|
|
787
|
+
encodedUserId: string,
|
|
788
|
+
specifier: string,
|
|
789
|
+
oracleAddress: string,
|
|
790
|
+
discoveryProviderEndpoint: string
|
|
791
|
+
) {
|
|
792
|
+
const req = Requests.getChallengeAttestation(
|
|
793
|
+
challengeId,
|
|
794
|
+
encodedUserId,
|
|
795
|
+
specifier,
|
|
796
|
+
oracleAddress
|
|
797
|
+
)
|
|
798
|
+
const { data } = await this._performRequestWithMonitoring(
|
|
799
|
+
req,
|
|
800
|
+
discoveryProviderEndpoint
|
|
801
|
+
)
|
|
802
|
+
return data
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
async getCreateSenderAttestation(
|
|
806
|
+
senderEthAddress: string,
|
|
807
|
+
discoveryProviderEndpoint: string
|
|
808
|
+
) {
|
|
809
|
+
const req = Requests.getCreateSenderAttestation(senderEthAddress)
|
|
810
|
+
const { data } = await this._performRequestWithMonitoring(
|
|
811
|
+
req,
|
|
812
|
+
discoveryProviderEndpoint
|
|
813
|
+
)
|
|
814
|
+
return data
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async getUndisbursedChallenges(
|
|
818
|
+
limit: number | null = null,
|
|
819
|
+
offset: number | null = null,
|
|
820
|
+
completedBlockNumber: string | null = null,
|
|
821
|
+
encodedUserId: number | null = null
|
|
822
|
+
) {
|
|
823
|
+
const req = Requests.getUndisbursedChallenges(
|
|
824
|
+
limit,
|
|
825
|
+
offset,
|
|
826
|
+
completedBlockNumber,
|
|
827
|
+
encodedUserId
|
|
828
|
+
)
|
|
829
|
+
const res = await this._makeRequest<Array<{ amount: string }>>(req)
|
|
830
|
+
if (!res) return []
|
|
831
|
+
return res.map((r) => ({ ...r, amount: parseInt(r.amount) }))
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/* ------- INTERNAL FUNCTIONS ------- */
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Performs a single request, defined in the request, via axios, calling any
|
|
838
|
+
* monitoring callbacks as needed.
|
|
839
|
+
*
|
|
840
|
+
* @param {{
|
|
841
|
+
endpoint: string,
|
|
842
|
+
urlParams: string,
|
|
843
|
+
queryParams: object,
|
|
844
|
+
method: string,
|
|
845
|
+
headers: object,
|
|
846
|
+
}} requestObj
|
|
847
|
+
* @param {string} discoveryProviderEndpoint
|
|
848
|
+
* @returns
|
|
849
|
+
* @memberof DiscoveryProvider
|
|
850
|
+
*/
|
|
851
|
+
async _performRequestWithMonitoring(
|
|
852
|
+
requestObj: RequestParams,
|
|
853
|
+
discoveryProviderEndpoint: string
|
|
854
|
+
) {
|
|
855
|
+
const axiosRequest = this._createDiscProvRequest(
|
|
856
|
+
requestObj,
|
|
857
|
+
discoveryProviderEndpoint
|
|
858
|
+
)
|
|
859
|
+
let response
|
|
860
|
+
let parsedResponse
|
|
861
|
+
|
|
862
|
+
const url = new URL(axiosRequest.url ?? '')
|
|
863
|
+
const start = Date.now()
|
|
864
|
+
try {
|
|
865
|
+
response = await axios(axiosRequest)
|
|
866
|
+
const duration = Date.now() - start
|
|
867
|
+
parsedResponse = Utils.parseDataFromResponse(response)
|
|
868
|
+
|
|
869
|
+
// Fire monitoring callbacks for request success case
|
|
870
|
+
if (this.monitoringCallbacks && 'request' in this.monitoringCallbacks) {
|
|
871
|
+
try {
|
|
872
|
+
this.monitoringCallbacks.request({
|
|
873
|
+
endpoint: url.origin,
|
|
874
|
+
pathname: url.pathname,
|
|
875
|
+
queryString: url.search,
|
|
876
|
+
signer: response.data.signer,
|
|
877
|
+
signature: response.data.signature,
|
|
878
|
+
requestMethod: axiosRequest.method,
|
|
879
|
+
status: response.status,
|
|
880
|
+
responseTimeMillis: duration
|
|
881
|
+
})
|
|
882
|
+
} catch (e) {
|
|
883
|
+
// Swallow errors -- this method should not throw generally
|
|
884
|
+
console.error(e)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
} catch (e) {
|
|
888
|
+
const error = e as AxiosError
|
|
889
|
+
const resp = error.response
|
|
890
|
+
const duration = Date.now() - start
|
|
891
|
+
const errMsg = error.response?.data ?? error
|
|
892
|
+
|
|
893
|
+
// Fire monitoring callbaks for request failure case
|
|
894
|
+
if (this.monitoringCallbacks && 'request' in this.monitoringCallbacks) {
|
|
895
|
+
try {
|
|
896
|
+
this.monitoringCallbacks.request({
|
|
897
|
+
endpoint: url.origin,
|
|
898
|
+
pathname: url.pathname,
|
|
899
|
+
queryString: url.search,
|
|
900
|
+
requestMethod: axiosRequest.method,
|
|
901
|
+
status: resp?.status,
|
|
902
|
+
responseTimeMillis: duration
|
|
903
|
+
})
|
|
904
|
+
} catch (e) {
|
|
905
|
+
// Swallow errors -- this method should not throw generally
|
|
906
|
+
console.error(e)
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (resp && resp.status === 404) {
|
|
910
|
+
// We have 404'd. Throw that error message back out
|
|
911
|
+
throw new Error('404')
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
throw errMsg
|
|
915
|
+
}
|
|
916
|
+
return parsedResponse
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Gets how many blocks behind a discovery node is.
|
|
921
|
+
* If this method throws (missing data in health check response),
|
|
922
|
+
* return an unhealthy number of blocks
|
|
923
|
+
* @param parsedResponse health check response object
|
|
924
|
+
* @returns a number of blocks if behind or null if not behind
|
|
925
|
+
*/
|
|
926
|
+
async _getBlocksBehind(parsedResponse: {
|
|
927
|
+
latest_indexed_block: number
|
|
928
|
+
latest_chain_block: number
|
|
929
|
+
}) {
|
|
930
|
+
try {
|
|
931
|
+
const {
|
|
932
|
+
latest_indexed_block: indexedBlock,
|
|
933
|
+
latest_chain_block: chainBlock
|
|
934
|
+
} = parsedResponse
|
|
935
|
+
|
|
936
|
+
const blockDiff = chainBlock - indexedBlock
|
|
937
|
+
if (blockDiff > this.unhealthyBlockDiff) {
|
|
938
|
+
return blockDiff
|
|
939
|
+
}
|
|
940
|
+
return null
|
|
941
|
+
} catch (e) {
|
|
942
|
+
console.error(e)
|
|
943
|
+
return this.unhealthyBlockDiff
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Gets how many plays slots behind a discovery node is.
|
|
949
|
+
* If this method throws (missing data in health check response),
|
|
950
|
+
* return an unhealthy number of slots
|
|
951
|
+
* @param parsedResponse health check response object
|
|
952
|
+
* @returns a number of slots if behind or null if not behind
|
|
953
|
+
*/
|
|
954
|
+
async _getPlaysSlotsBehind(parsedResponse: {
|
|
955
|
+
latest_indexed_slot_plays: number
|
|
956
|
+
latest_chain_slot_plays: number
|
|
957
|
+
}) {
|
|
958
|
+
if (!this.unhealthySlotDiffPlays) return null
|
|
959
|
+
|
|
960
|
+
try {
|
|
961
|
+
const {
|
|
962
|
+
latest_indexed_slot_plays: indexedSlotPlays,
|
|
963
|
+
latest_chain_slot_plays: chainSlotPlays
|
|
964
|
+
} = parsedResponse
|
|
965
|
+
|
|
966
|
+
const slotDiff = chainSlotPlays - indexedSlotPlays
|
|
967
|
+
if (slotDiff > this.unhealthySlotDiffPlays) {
|
|
968
|
+
return slotDiff
|
|
969
|
+
}
|
|
970
|
+
return null
|
|
971
|
+
} catch (e) {
|
|
972
|
+
console.error(e)
|
|
973
|
+
return this.unhealthySlotDiffPlays
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Makes a request to a discovery node, reselecting if necessary
|
|
979
|
+
* @param {{
|
|
980
|
+
* endpoint: string
|
|
981
|
+
* urlParams: object
|
|
982
|
+
* queryParams: object
|
|
983
|
+
* method: string
|
|
984
|
+
* headers: object
|
|
985
|
+
* }} {
|
|
986
|
+
* endpoint: the base route
|
|
987
|
+
* urlParams: string of URL params to be concatenated after base route
|
|
988
|
+
* queryParams: URL query (search) params
|
|
989
|
+
* method: string HTTP method
|
|
990
|
+
* }
|
|
991
|
+
* @param retry whether to retry on failure
|
|
992
|
+
* @param attemptedRetries number of attempted retries (stops retrying at max)
|
|
993
|
+
*/
|
|
994
|
+
async _makeRequest<Response>(
|
|
995
|
+
requestObj: Record<string, unknown>,
|
|
996
|
+
retry = true,
|
|
997
|
+
attemptedRetries = 0
|
|
998
|
+
): Promise<Response | undefined | null> {
|
|
999
|
+
try {
|
|
1000
|
+
const newDiscProvEndpoint =
|
|
1001
|
+
await this.getHealthyDiscoveryProviderEndpoint(attemptedRetries)
|
|
1002
|
+
|
|
1003
|
+
// If new DP endpoint is selected, update disc prov endpoint and reset attemptedRetries count
|
|
1004
|
+
if (this.discoveryProviderEndpoint !== newDiscProvEndpoint) {
|
|
1005
|
+
let updateDiscProvEndpointMsg = `Current Discovery Provider endpoint ${this.discoveryProviderEndpoint} is unhealthy. `
|
|
1006
|
+
updateDiscProvEndpointMsg += `Switching over to the new Discovery Provider endpoint ${newDiscProvEndpoint}!`
|
|
1007
|
+
console.info(updateDiscProvEndpointMsg)
|
|
1008
|
+
this.discoveryProviderEndpoint = newDiscProvEndpoint
|
|
1009
|
+
attemptedRetries = 0
|
|
1010
|
+
}
|
|
1011
|
+
} catch (e) {
|
|
1012
|
+
console.error(e)
|
|
1013
|
+
return
|
|
1014
|
+
}
|
|
1015
|
+
let parsedResponse
|
|
1016
|
+
try {
|
|
1017
|
+
parsedResponse = await this._performRequestWithMonitoring(
|
|
1018
|
+
requestObj as RequestParams,
|
|
1019
|
+
this.discoveryProviderEndpoint
|
|
1020
|
+
)
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
const error = e as Error
|
|
1023
|
+
const failureStr = 'Failed to make Discovery Provider request, '
|
|
1024
|
+
const attemptStr = `attempt #${attemptedRetries}, `
|
|
1025
|
+
const errorStr = `error ${JSON.stringify(error.message)}, `
|
|
1026
|
+
const requestStr = `request: ${JSON.stringify(requestObj)}`
|
|
1027
|
+
const fullErrString = `${failureStr}${attemptStr}${errorStr}${requestStr}`
|
|
1028
|
+
|
|
1029
|
+
console.warn(fullErrString)
|
|
1030
|
+
|
|
1031
|
+
if (retry) {
|
|
1032
|
+
if (error.message === '404') {
|
|
1033
|
+
this.request404Count += 1
|
|
1034
|
+
if (this.request404Count < this.maxRequestsForTrue404) {
|
|
1035
|
+
// In the case of a 404, retry with a different discovery node entirely
|
|
1036
|
+
// using selectionRequestRetries + 1 to force reselection
|
|
1037
|
+
return await this._makeRequest(
|
|
1038
|
+
requestObj,
|
|
1039
|
+
retry,
|
|
1040
|
+
this.selectionRequestRetries + 1
|
|
1041
|
+
)
|
|
1042
|
+
} else {
|
|
1043
|
+
this.request404Count = 0
|
|
1044
|
+
return null
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// In the case of an unknown error, retry with attempts += 1
|
|
1049
|
+
return await this._makeRequest(requestObj, retry, attemptedRetries + 1)
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return null
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Validate health check response
|
|
1056
|
+
|
|
1057
|
+
// Regressed mode signals we couldn't find a node that wasn't behind by some measure
|
|
1058
|
+
// so we should should pick something
|
|
1059
|
+
const notInRegressedMode =
|
|
1060
|
+
this.ethContracts && !this.ethContracts.isInRegressedMode()
|
|
1061
|
+
|
|
1062
|
+
const blockDiff = await this._getBlocksBehind(parsedResponse)
|
|
1063
|
+
if (notInRegressedMode && blockDiff) {
|
|
1064
|
+
if (retry) {
|
|
1065
|
+
console.info(
|
|
1066
|
+
`${this.discoveryProviderEndpoint} is too far behind [block diff: ${blockDiff}]. Retrying request at attempt #${attemptedRetries}...`
|
|
1067
|
+
)
|
|
1068
|
+
return await this._makeRequest(requestObj, retry, attemptedRetries + 1)
|
|
1069
|
+
}
|
|
1070
|
+
return null
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const playsSlotDiff = await this._getPlaysSlotsBehind(parsedResponse)
|
|
1074
|
+
if (notInRegressedMode && playsSlotDiff) {
|
|
1075
|
+
if (retry) {
|
|
1076
|
+
console.info(
|
|
1077
|
+
`${this.discoveryProviderEndpoint} is too far behind [slot diff: ${playsSlotDiff}]. Retrying request at attempt #${attemptedRetries}...`
|
|
1078
|
+
)
|
|
1079
|
+
return await this._makeRequest(requestObj, retry, attemptedRetries + 1)
|
|
1080
|
+
}
|
|
1081
|
+
return null
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Reset 404 counts
|
|
1085
|
+
this.request404Count = 0
|
|
1086
|
+
|
|
1087
|
+
// Everything looks good, return the data!
|
|
1088
|
+
return parsedResponse.data
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Gets the healthy discovery provider endpoint used in creating the axios request later.
|
|
1093
|
+
* If the number of retries is over the max count for retires, clear the cache and reselect
|
|
1094
|
+
* another healthy discovery provider. Else, return the current discovery provider endpoint
|
|
1095
|
+
* @param attemptedRetries the number of attempted requests made to the current disc prov endpoint
|
|
1096
|
+
*/
|
|
1097
|
+
async getHealthyDiscoveryProviderEndpoint(attemptedRetries: number) {
|
|
1098
|
+
let endpoint = this.discoveryProviderEndpoint as string
|
|
1099
|
+
if (attemptedRetries > this.selectionRequestRetries) {
|
|
1100
|
+
// Add to unhealthy list if current disc prov endpoint has reached max retry count
|
|
1101
|
+
console.info(`Attempted max retries with endpoint ${endpoint}`)
|
|
1102
|
+
this.serviceSelector.addUnhealthy(endpoint)
|
|
1103
|
+
|
|
1104
|
+
// Clear the cached endpoint and select new endpoint from backups
|
|
1105
|
+
this.serviceSelector.clearCached()
|
|
1106
|
+
endpoint = await this.serviceSelector.select()
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// If there are no more available backups, throw error
|
|
1110
|
+
if (!endpoint) {
|
|
1111
|
+
throw new Error('All Discovery Providers are unhealthy and unavailable.')
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
return endpoint
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Creates the discovery provider axios request object with necessary configs
|
|
1119
|
+
* @param requestObj
|
|
1120
|
+
* @param discoveryProviderEndpoint
|
|
1121
|
+
*/
|
|
1122
|
+
_createDiscProvRequest(
|
|
1123
|
+
requestObj: RequestParams,
|
|
1124
|
+
discoveryProviderEndpoint: string
|
|
1125
|
+
) {
|
|
1126
|
+
// Sanitize URL params if needed
|
|
1127
|
+
if (requestObj.queryParams) {
|
|
1128
|
+
Object.entries(requestObj.queryParams).forEach(([k, v]) => {
|
|
1129
|
+
if (v === undefined || v === null) {
|
|
1130
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
1131
|
+
delete requestObj.queryParams[k]
|
|
1132
|
+
}
|
|
1133
|
+
})
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const requestUrl = urlJoin(
|
|
1137
|
+
discoveryProviderEndpoint,
|
|
1138
|
+
requestObj.endpoint,
|
|
1139
|
+
requestObj.urlParams,
|
|
1140
|
+
{ query: requestObj.queryParams }
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
let headers: Record<string, string> = {}
|
|
1144
|
+
if (requestObj.headers) {
|
|
1145
|
+
headers = requestObj.headers
|
|
1146
|
+
}
|
|
1147
|
+
const currentUserId = this.userStateManager.getCurrentUserId()
|
|
1148
|
+
if (currentUserId) {
|
|
1149
|
+
headers['X-User-ID'] = currentUserId
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const timeout = requestObj.timeout ?? this.selectionRequestTimeout
|
|
1153
|
+
let axiosRequest: AxiosRequestConfig = {
|
|
1154
|
+
url: requestUrl,
|
|
1155
|
+
headers: headers,
|
|
1156
|
+
method: requestObj.method ?? 'get',
|
|
1157
|
+
timeout
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (requestObj.method === 'post' && requestObj.data) {
|
|
1161
|
+
axiosRequest = {
|
|
1162
|
+
...axiosRequest,
|
|
1163
|
+
data: requestObj.data
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return axiosRequest
|
|
1167
|
+
}
|
|
1168
|
+
}
|