Skip to content

Commit 7dedb21

Browse files
committed
Refactor subgraph options to enforce complete retry configuration and update related tests
1 parent 4e9e51a commit 7dedb21

File tree

7 files changed

+60
-23
lines changed

7 files changed

+60
-23
lines changed

packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
------
2525
"""
2626

27-
from datetime import datetime
2827
import logging
2928
from typing import List, Optional
3029

packages/sdk/python/human-protocol-sdk/human_protocol_sdk/utils.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import time
55
import re
6-
from typing import Tuple, Optional, Dict, Any
6+
from typing import Tuple, Optional
77
from dataclasses import dataclass
88

99
import requests
@@ -37,8 +37,8 @@
3737
class SubgraphOptions:
3838
"""Configuration for subgraph logic."""
3939

40-
max_retries: int = 3
41-
base_delay: int = 1000 # milliseconds
40+
max_retries: Optional[int] = None
41+
base_delay: Optional[int] = None # milliseconds
4242

4343

4444
def is_indexer_error(error: Exception) -> bool:
@@ -94,7 +94,17 @@ def custom_gql_fetch(
9494
if not options:
9595
return _fetch_subgraph_data(network, query, params)
9696

97-
max_retries = options.max_retries
97+
if (
98+
options.max_retries is not None
99+
and options.base_delay is None
100+
or options.max_retries is None
101+
and options.base_delay is not None
102+
):
103+
raise ValueError(
104+
"Retry configuration must include both max_retries and base_delay"
105+
)
106+
107+
max_retries = int(options.max_retries)
98108
base_delay = options.base_delay / 1000
99109

100110
last_error = None

packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/test_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ def test_retries_on_indexer_error_and_succeeds(self):
8181
self.assertEqual(mock_fetch.call_count, 2)
8282
mock_sleep.assert_called_once()
8383

84+
def test_raises_when_retry_options_incomplete(self):
85+
options = SubgraphOptions(max_retries=2)
86+
87+
with patch("human_protocol_sdk.utils._fetch_subgraph_data") as mock_fetch:
88+
with self.assertRaises(ValueError) as ctx:
89+
custom_gql_fetch(
90+
self.network, self.query, self.variables, options=options
91+
)
92+
93+
self.assertIn("max_retries", str(ctx.exception))
94+
mock_fetch.assert_not_called()
95+
8496
def test_raises_immediately_on_non_indexer_error(self):
8597
options = SubgraphOptions(max_retries=3, base_delay=50)
8698
with patch(

packages/sdk/typescript/human-protocol-sdk/src/error.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ export const ErrorBulkPayOutVersion = new Error(
306306
'Invalid bulkPayOut parameters for the contract version of the specified escrow address'
307307
);
308308

309+
/**
310+
* @constant {Error} - Retry configuration is missing required parameters.
311+
*/
312+
export const ErrorRetryParametersMissing = new Error(
313+
'Retry configuration must include both maxRetries and baseDelay'
314+
);
315+
309316
/**
310317
* @constant {Warning} - Possible version mismatch.
311318
*/

packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,8 @@ export interface IEscrowWithdraw {
317317
* Configuration options for subgraph requests with retry logic.
318318
*/
319319
export interface SubgraphOptions {
320-
/** Maximum number of retry attempts (default: 3) */
320+
/** Maximum number of retry attempts */
321321
maxRetries?: number;
322-
/** Base delay between retries in milliseconds (default: 1000) */
322+
/** Base delay between retries in milliseconds */
323323
baseDelay?: number;
324324
}

packages/sdk/typescript/human-protocol-sdk/src/utils.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { SUBGRAPH_API_KEY_PLACEHOLDER } from './constants';
77
import { ChainId } from './enums';
88
import {
99
ContractExecutionError,
10+
ErrorRetryParametersMissing,
1011
EthereumError,
1112
InvalidArgumentError,
1213
NonceExpired,
@@ -131,27 +132,31 @@ export const customGqlFetch = async <T = any>(
131132
return await gqlFetch<T>(url, query, variables);
132133
}
133134

134-
const maxRetries = options.maxRetries ?? 3;
135-
const baseDelay = options.baseDelay ?? 1000;
135+
if (
136+
(options.maxRetries && options.baseDelay === undefined) ||
137+
(options.baseDelay && options.maxRetries === undefined)
138+
) {
139+
throw ErrorRetryParametersMissing;
140+
}
136141

137142
let lastError: any;
138143

139-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
144+
for (let attempt = 0; attempt <= (options.maxRetries as number); attempt++) {
140145
try {
141146
const result = await gqlFetch<T>(url, query, variables);
142147
return result;
143148
} catch (error) {
144149
lastError = error;
145150

146-
if (attempt === maxRetries) {
151+
if (attempt === options.maxRetries) {
147152
throw error;
148153
}
149154

150155
if (!isIndexerError(error)) {
151156
throw error;
152157
}
153158

154-
const delay = baseDelay * attempt;
159+
const delay = (options.baseDelay as number) * attempt;
155160
await sleep(delay);
156161
}
157162
}

packages/sdk/typescript/human-protocol-sdk/test/utils.test.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ReplacementUnderpriced,
2020
TransactionReplaced,
2121
WarnSubgraphApiKeyNotProvided,
22+
ErrorRetryParametersMissing,
2223
} from '../src/error';
2324
import {
2425
getSubgraphUrl,
@@ -298,21 +299,24 @@ describe('customGqlFetch', () => {
298299
expect(gqlFetchSpy).toHaveBeenCalledTimes(3);
299300
});
300301

301-
test('uses default values for missing maxRetries', async () => {
302-
const badIndexerError = {
303-
response: {
304-
errors: [{ message: 'bad indexers: {0x123: Timeout}' }],
305-
},
306-
};
307-
const gqlFetchSpy = vi
308-
.spyOn(gqlFetch, 'default')
309-
.mockRejectedValue(badIndexerError);
302+
test('throws when retry options are incomplete', async () => {
303+
const gqlFetchSpy = vi.spyOn(gqlFetch, 'default');
310304

311305
await expect(
312306
customGqlFetch(mockUrl, mockQuery, mockVariables, { baseDelay: 10 })
313-
).rejects.toEqual(badIndexerError);
307+
).rejects.toBe(ErrorRetryParametersMissing);
308+
309+
expect(gqlFetchSpy).not.toHaveBeenCalled();
310+
});
311+
312+
test('throws when only maxRetries is provided', async () => {
313+
const gqlFetchSpy = vi.spyOn(gqlFetch, 'default');
314+
315+
await expect(
316+
customGqlFetch(mockUrl, mockQuery, mockVariables, { maxRetries: 1 })
317+
).rejects.toBe(ErrorRetryParametersMissing);
314318

315-
expect(gqlFetchSpy).toHaveBeenCalledTimes(4);
319+
expect(gqlFetchSpy).not.toHaveBeenCalled();
316320
});
317321

318322
test('uses custom maxRetries when provided', async () => {

0 commit comments

Comments
 (0)