Skip to content

Commit bcbf691

Browse files
committed
default block time range
1 parent 8d15fa9 commit bcbf691

File tree

10 files changed

+208
-7
lines changed

10 files changed

+208
-7
lines changed

api/api.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import (
77
"reflect"
88
"strconv"
99
"strings"
10+
"time"
1011

1112
"github.com/gin-gonic/gin"
1213
"github.com/gorilla/schema"
1314
"github.com/rs/zerolog/log"
15+
config "github.com/thirdweb-dev/indexer/configs"
16+
"github.com/thirdweb-dev/indexer/internal/storage"
1417
)
1518

1619
// Error represents an API error response
@@ -155,3 +158,44 @@ func ParseIntQueryParam(value string, defaultValue int) int {
155158
}
156159
return parsed
157160
}
161+
162+
// ApplyDefaultTimeRange applies default time range filters (now to N days ago)
163+
// if no block timestamp filters are already present in the filter params
164+
func ApplyDefaultTimeRange(filterParams map[string]string) {
165+
// Check if any block timestamp filters are already present
166+
hasTimestampFilter := false
167+
for key := range filterParams {
168+
if strings.Contains(key, "block_timestamp") {
169+
hasTimestampFilter = true
170+
break
171+
}
172+
}
173+
174+
// If no timestamp filters are present, apply default range
175+
if !hasTimestampFilter {
176+
now := time.Now()
177+
178+
// Get default time range from config, fallback to 7 days if not set
179+
defaultDays := 7
180+
if config.Cfg.API.DefaultTimeRangeDays > 0 {
181+
defaultDays = config.Cfg.API.DefaultTimeRangeDays
182+
}
183+
184+
defaultTimeAgo := now.AddDate(0, 0, -defaultDays)
185+
186+
filterParams["block_timestamp_gte"] = strconv.FormatInt(defaultTimeAgo.Unix(), 10)
187+
filterParams["block_timestamp_lte"] = strconv.FormatInt(now.Unix(), 10)
188+
}
189+
}
190+
191+
// ApplyDefaultTimeRangeToTransfers applies default time range to TransfersQueryFilter
192+
// by converting time range to block number range if no block number filters are provided
193+
func ApplyDefaultTimeRangeToTransfers(qf *storage.TransfersQueryFilter) {
194+
// Only apply if no block number filters are already set
195+
if qf.StartBlockNumber == nil && qf.EndBlockNumber == nil {
196+
// For transfers, we'll use a conservative approach and not apply time-based defaults
197+
// since transfers already have block number filtering and the conversion from time to block
198+
// would require additional RPC calls to get block numbers for timestamps
199+
// This can be implemented later if needed
200+
}
201+
}

api/api_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package api
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
"time"
7+
8+
config "github.com/thirdweb-dev/indexer/configs"
9+
)
10+
11+
func TestApplyDefaultTimeRange(t *testing.T) {
12+
// Save original config
13+
originalConfig := config.Cfg
14+
15+
// Test with different default values
16+
testCases := []struct {
17+
name string
18+
defaultDays int
19+
inputParams map[string]string
20+
expectedParams map[string]string
21+
shouldApply bool
22+
}{
23+
{
24+
name: "no timestamp filters - should apply defaults (7 days)",
25+
defaultDays: 7,
26+
inputParams: map[string]string{"other_filter": "value"},
27+
shouldApply: true,
28+
},
29+
{
30+
name: "no timestamp filters - should apply defaults (3 days)",
31+
defaultDays: 3,
32+
inputParams: map[string]string{"other_filter": "value"},
33+
shouldApply: true,
34+
},
35+
{
36+
name: "has block_timestamp_gte - should not apply defaults",
37+
defaultDays: 7,
38+
inputParams: map[string]string{"block_timestamp_gte": "1234567890"},
39+
shouldApply: false,
40+
},
41+
{
42+
name: "has block_timestamp_lte - should not apply defaults",
43+
defaultDays: 7,
44+
inputParams: map[string]string{"block_timestamp_lte": "1234567890"},
45+
shouldApply: false,
46+
},
47+
{
48+
name: "has other block_timestamp filter - should not apply defaults",
49+
defaultDays: 7,
50+
inputParams: map[string]string{"block_timestamp_other": "1234567890"},
51+
shouldApply: false,
52+
},
53+
}
54+
55+
for _, tt := range testCases {
56+
t.Run(tt.name, func(t *testing.T) {
57+
// Set test config
58+
config.Cfg.API.DefaultTimeRangeDays = tt.defaultDays
59+
60+
// Create a copy of input params
61+
params := make(map[string]string)
62+
for k, v := range tt.inputParams {
63+
params[k] = v
64+
}
65+
66+
// Apply default time range
67+
ApplyDefaultTimeRange(params)
68+
69+
// Check if defaults were applied
70+
hadGteBefore := false
71+
hadLteBefore := false
72+
hasGteAfter := false
73+
hasLteAfter := false
74+
75+
// Check what was in input
76+
for key := range tt.inputParams {
77+
if key == "block_timestamp_gte" {
78+
hadGteBefore = true
79+
}
80+
if key == "block_timestamp_lte" {
81+
hadLteBefore = true
82+
}
83+
}
84+
85+
// Check what's in output
86+
for key := range params {
87+
if key == "block_timestamp_gte" {
88+
hasGteAfter = true
89+
}
90+
if key == "block_timestamp_lte" {
91+
hasLteAfter = true
92+
}
93+
}
94+
95+
if tt.shouldApply {
96+
// Should have added both gte and lte
97+
if !hasGteAfter || !hasLteAfter {
98+
t.Errorf("Expected default time range to be applied, but gte=%v, lte=%v", hasGteAfter, hasLteAfter)
99+
}
100+
} else {
101+
// Should not have added any new defaults
102+
if (!hadGteBefore && hasGteAfter) || (!hadLteBefore && hasLteAfter) {
103+
t.Errorf("Expected no new defaults to be applied, but added gte=%v, lte=%v",
104+
!hadGteBefore && hasGteAfter, !hadLteBefore && hasLteAfter)
105+
}
106+
}
107+
108+
// Verify the time range is reasonable
109+
if hasGteAfter && hasLteAfter {
110+
now := time.Now()
111+
maxDaysAgo := now.AddDate(0, 0, -(tt.defaultDays + 1))
112+
113+
// Parse the timestamps
114+
if gteStr, exists := params["block_timestamp_gte"]; exists {
115+
if gteUnix, err := strconv.ParseInt(gteStr, 10, 64); err == nil {
116+
gte := time.Unix(gteUnix, 0)
117+
if gte.Before(maxDaysAgo) {
118+
t.Errorf("block_timestamp_gte is too old: %v (expected within %d days)", gte, tt.defaultDays)
119+
}
120+
}
121+
}
122+
123+
if lteStr, exists := params["block_timestamp_lte"]; exists {
124+
if lteUnix, err := strconv.ParseInt(lteStr, 10, 64); err == nil {
125+
lte := time.Unix(lteUnix, 0)
126+
if lte.After(now) {
127+
t.Errorf("block_timestamp_lte is in the future: %v", lte)
128+
}
129+
}
130+
}
131+
}
132+
})
133+
}
134+
135+
// Restore original config
136+
config.Cfg = originalConfig
137+
}

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func init() {
114114
rootCmd.PersistentFlags().Int("api-contractApiRequest-idleConnTimeout", 90, "Idle connection timeout for contract API request in seconds")
115115
rootCmd.PersistentFlags().Bool("api-contractApiRequest-disableCompression", false, "Disable compression for contract API request")
116116
rootCmd.PersistentFlags().Int("api-contractApiRequest-timeout", 10, "Timeout in seconds for contract API request")
117+
rootCmd.PersistentFlags().Int("api-defaultTimeRangeDays", 7, "Default time range in days when no timestamp filters are provided")
117118
rootCmd.PersistentFlags().Bool("publisher-enabled", false, "Toggle publisher")
118119
rootCmd.PersistentFlags().String("publisher-brokers", "", "Kafka brokers")
119120
rootCmd.PersistentFlags().Bool("publisher-blocks-enabled", false, "Toggle block publisher")
@@ -209,6 +210,7 @@ func init() {
209210
viper.BindPFlag("api.contractApiRequest.idleConnTimeout", rootCmd.PersistentFlags().Lookup("api-contractApiRequest-idleConnTimeout"))
210211
viper.BindPFlag("api.contractApiRequest.disableCompression", rootCmd.PersistentFlags().Lookup("api-contractApiRequest-disableCompression"))
211212
viper.BindPFlag("api.contractApiRequest.timeout", rootCmd.PersistentFlags().Lookup("api-contractApiRequest-timeout"))
213+
viper.BindPFlag("api.defaultTimeRangeDays", rootCmd.PersistentFlags().Lookup("api-defaultTimeRangeDays"))
212214
viper.BindPFlag("publisher.enabled", rootCmd.PersistentFlags().Lookup("publisher-enabled"))
213215
viper.BindPFlag("publisher.brokers", rootCmd.PersistentFlags().Lookup("publisher-brokers"))
214216
viper.BindPFlag("publisher.blocks.enabled", rootCmd.PersistentFlags().Lookup("publisher-blocks-enabled"))

configs/config.example.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ reorgHandler:
4141
validation:
4242
mode: strict # "disabled", "minimal", or "strict"
4343

44+
api:
45+
host: ":8080"
46+
defaultTimeRangeDays: 7 # Default time range in days when no timestamp filters are provided
47+
4448
storage:
4549
main:
4650
clickhouse:

configs/config.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,13 @@ type ContractApiRequestConfig struct {
124124
}
125125

126126
type APIConfig struct {
127-
Host string `mapstructure:"host"`
128-
BasicAuth BasicAuthConfig `mapstructure:"basicAuth"`
129-
ThirdwebContractApi string `mapstructure:"thirdwebContractApi"`
130-
ContractApiRequest ContractApiRequestConfig `mapstructure:"contractApiRequest"`
131-
AbiDecodingEnabled bool `mapstructure:"abiDecodingEnabled"`
132-
Thirdweb ThirdwebConfig `mapstructure:"thirdweb"`
127+
Host string `mapstructure:"host"`
128+
BasicAuth BasicAuthConfig `mapstructure:"basicAuth"`
129+
ThirdwebContractApi string `mapstructure:"thirdwebContractApi"`
130+
ContractApiRequest ContractApiRequestConfig `mapstructure:"contractApiRequest"`
131+
AbiDecodingEnabled bool `mapstructure:"abiDecodingEnabled"`
132+
Thirdweb ThirdwebConfig `mapstructure:"thirdweb"`
133+
DefaultTimeRangeDays int `mapstructure:"defaultTimeRangeDays"`
133134
}
134135

135136
type BlockPublisherConfig struct {

indexer

50.3 MB
Binary file not shown.

internal/handlers/blocks_handlers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ func handleBlocksRequest(c *gin.Context) {
6363
ForceConsistentData: queryParams.ForceConsistentData,
6464
}
6565

66+
// Apply default time range if no block timestamp filters are provided
67+
api.ApplyDefaultTimeRange(qf.FilterParams)
68+
6669
// Initialize the QueryResult
6770
queryResult := api.QueryResponse{
6871
Meta: api.Meta{

internal/handlers/logs_handlers.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,9 @@ func handleLogsRequest(c *gin.Context) {
117117
eventABI, err = common.ConstructEventABI(signature)
118118
if err != nil {
119119
log.Debug().Err(err).Msgf("Unable to construct event ABI for %s", signature)
120+
} else {
121+
signatureHash = eventABI.ID.Hex()
120122
}
121-
signatureHash = eventABI.ID.Hex()
122123
}
123124

124125
mainStorage, err := getMainStorage()
@@ -141,6 +142,9 @@ func handleLogsRequest(c *gin.Context) {
141142
ForceConsistentData: queryParams.ForceConsistentData,
142143
}
143144

145+
// Apply default time range if no block timestamp filters are provided
146+
api.ApplyDefaultTimeRange(qf.FilterParams)
147+
144148
// Initialize the QueryResult
145149
queryResult := api.QueryResponse{
146150
Meta: api.Meta{

internal/handlers/transactions_handlers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ func handleTransactionsRequest(c *gin.Context) {
160160
ForceConsistentData: queryParams.ForceConsistentData,
161161
}
162162

163+
// Apply default time range if no block timestamp filters are provided
164+
api.ApplyDefaultTimeRange(qf.FilterParams)
165+
163166
// Initialize the QueryResult
164167
queryResult := api.QueryResponse{
165168
Meta: api.Meta{

internal/handlers/transfer_handlers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ func GetTokenTransfers(c *gin.Context) {
125125
SortOrder: c.Query("sort_order"),
126126
}
127127

128+
// Apply default time range if no block number filters are provided
129+
api.ApplyDefaultTimeRangeToTransfers(&qf)
130+
128131
// Define columns for query
129132
columns := []string{
130133
"token_type",

0 commit comments

Comments
 (0)