Skip to content

Commit f7f11bc

Browse files
authored
Refactor API handlers and implement storage queries (#43)
# API Enhancements and Storage Integration ## Changes - Renamed `Aggregate` to `Aggregates` in `QueryParams` struct - Updated route handler names for better clarity: - `GetTransactionsWithContract` to `GetTransactionsByContract` - `GetEventsWithContract` to `GetLogsByContract` - `GetTransactionsWithContractAndSignature` to `GetTransactionsByContractAndSignature` - `GetEventsWithContractAndSignature` to `GetLogsByContractAndSignature` - Implemented `handleLogsRequest` and `handleTransactionsRequest` functions to centralize logic - Integrated storage layer with API handlers: - Added `getMainStorage` function to create storage connector - Updated `GetLogs` and `GetTransactions` methods to use storage queries - Enhanced `QueryFilter` struct with additional fields for filtering and aggregation - Modified `ClickHouseConnector` methods to remove block number filtering and prepare for more flexible querying - Renamed `newConnector` to `NewConnector` and made it public ## TODO - Implement total items count and total pages count in API responses - Add aggregations, filters, and grouping to log queries in ClickHouse connector
1 parent 8a5aff6 commit f7f11bc

File tree

8 files changed

+126
-164
lines changed

8 files changed

+126
-164
lines changed

api/api.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type QueryParams struct {
2525
SortOrder string `schema:"sort_order"`
2626
Page int `schema:"page"`
2727
Limit int `schema:"limit"`
28-
Aggregate []string `schema:"aggregate"`
28+
Aggregates []string `schema:"aggregate"`
2929
}
3030

3131
type Meta struct {

internal/common/transaction.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type Transaction struct {
1616
FromAddress string
1717
ToAddress string
1818
Value *big.Int
19-
Gas *big.Int
19+
Gas uint64
2020
GasPrice *big.Int
2121
Data string
2222
MaxFeePerGas *big.Int

internal/handlers/api.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ func Handler(r *chi.Mux) {
1818
router.Get("/{chainId}/events", GetLogs)
1919

2020
// contract scoped queries
21-
router.Get("/{chainId}/transactions/{contractAddress}", GetTransactionsWithContract)
22-
router.Get("/{chainId}/events/{contractAddress}", GetEventsWithContract)
21+
router.Get("/{chainId}/transactions/{contractAddress}", GetTransactionsByContract)
22+
router.Get("/{chainId}/events/{contractAddress}", GetLogsByContract)
2323

2424
// signature scoped queries
25-
router.Get("/{chainId}/transactions/{contractAddress}/{functionSig}", GetTransactionsWithContractAndSignature)
26-
router.Get("/{chainId}/events/{contractAddress}/{functionSig}", GetEventsWithContractAndSignature)
25+
router.Get("/{chainId}/transactions/{contractAddress}/{functionSig}", GetTransactionsByContractAndSignature)
26+
router.Get("/{chainId}/events/{contractAddress}/{functionSig}", GetLogsByContractAndSignature)
2727
})
2828

2929
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {

internal/handlers/logs_handlers.go

+61-71
Original file line numberDiff line numberDiff line change
@@ -3,119 +3,109 @@ package handlers
33
import (
44
"encoding/json"
55
"net/http"
6+
"sync"
67

78
"github.com/go-chi/chi/v5"
89
"github.com/rs/zerolog/log"
910
"github.com/thirdweb-dev/indexer/api"
11+
config "github.com/thirdweb-dev/indexer/configs"
12+
"github.com/thirdweb-dev/indexer/internal/storage"
13+
)
14+
15+
// package-level variables
16+
var (
17+
mainStorage storage.IMainStorage
18+
storageOnce sync.Once
19+
storageErr error
1020
)
1121

1222
func GetLogs(w http.ResponseWriter, r *http.Request) {
13-
chainId, err := api.GetChainId(r)
14-
if err != nil {
15-
api.BadRequestErrorHandler(w, err)
16-
return
17-
}
18-
queryParams, err := api.ParseQueryParams(r)
19-
if err != nil {
20-
api.BadRequestErrorHandler(w, err)
21-
return
22-
}
23+
handleLogsRequest(w, r, "", "")
24+
}
2325

24-
var response = api.QueryResponse{
25-
Meta: api.Meta{
26-
ChainIdentifier: chainId,
27-
ContractAddress: "todo",
28-
FunctionSig: "todo",
29-
Page: 1,
30-
Limit: 100,
31-
TotalItems: 0,
32-
TotalPages: 0,
33-
},
34-
Data: []interface{}{queryParams},
35-
}
26+
func GetLogsByContract(w http.ResponseWriter, r *http.Request) {
27+
contractAddress := chi.URLParam(r, "contractAddress")
28+
handleLogsRequest(w, r, contractAddress, "")
29+
}
3630

37-
w.Header().Set("Content-Type", "application/json")
38-
err = json.NewEncoder(w).Encode(response)
39-
if err != nil {
40-
log.Error().Err(err).Msg("Error encoding response")
41-
api.InternalErrorHandler(w)
42-
return
43-
}
31+
func GetLogsByContractAndSignature(w http.ResponseWriter, r *http.Request) {
32+
contractAddress := chi.URLParam(r, "contractAddress")
33+
functionSig := chi.URLParam(r, "functionSig")
34+
handleLogsRequest(w, r, contractAddress, functionSig)
4435
}
4536

46-
func GetEventsWithContract(w http.ResponseWriter, r *http.Request) {
37+
func handleLogsRequest(w http.ResponseWriter, r *http.Request, contractAddress, functionSig string) {
4738
chainId, err := api.GetChainId(r)
4839
if err != nil {
4940
api.BadRequestErrorHandler(w, err)
5041
return
5142
}
52-
contractAddress := chi.URLParam(r, "contractAddress")
43+
5344
queryParams, err := api.ParseQueryParams(r)
5445
if err != nil {
5546
api.BadRequestErrorHandler(w, err)
5647
return
5748
}
5849

59-
var response = api.QueryResponse{
60-
Meta: api.Meta{
61-
ChainIdentifier: chainId,
62-
ContractAddress: contractAddress,
63-
FunctionSig: "todo",
64-
Page: 1,
65-
Limit: 100,
66-
TotalItems: 0,
67-
TotalPages: 0,
68-
},
69-
Data: []interface{}{queryParams},
70-
}
71-
72-
w.Header().Set("Content-Type", "application/json")
73-
err = json.NewEncoder(w).Encode(response)
50+
mainStorage, err := getMainStorage()
7451
if err != nil {
75-
log.Error().Err(err).Msg("Error encoding response")
52+
log.Error().Err(err).Msg("Error getting main storage")
7653
api.InternalErrorHandler(w)
7754
return
7855
}
7956

80-
}
81-
82-
func GetEventsWithContractAndSignature(w http.ResponseWriter, r *http.Request) {
83-
chainId, err := api.GetChainId(r)
57+
logs, err := mainStorage.GetLogs(storage.QueryFilter{
58+
FilterParams: queryParams.FilterParams,
59+
GroupBy: queryParams.GroupBy,
60+
SortBy: queryParams.SortBy,
61+
SortOrder: queryParams.SortOrder,
62+
Page: queryParams.Page,
63+
Limit: queryParams.Limit,
64+
Aggregates: queryParams.Aggregates,
65+
ContractAddress: contractAddress,
66+
FunctionSig: functionSig,
67+
})
8468
if err != nil {
85-
api.BadRequestErrorHandler(w, err)
86-
return
87-
}
88-
contractAddress := chi.URLParam(r, "contractAddress")
89-
functionSig := chi.URLParam(r, "functionSig")
90-
queryParams, err := api.ParseQueryParams(r)
91-
if err != nil {
92-
api.BadRequestErrorHandler(w, err)
69+
log.Error().Err(err).Msg("Error querying logs")
70+
api.InternalErrorHandler(w)
9371
return
9472
}
9573

96-
var response = api.QueryResponse{
74+
response := api.QueryResponse{
9775
Meta: api.Meta{
9876
ChainIdentifier: chainId,
9977
ContractAddress: contractAddress,
10078
FunctionSig: functionSig,
101-
Page: 1,
102-
Limit: 100,
103-
TotalItems: 0,
104-
TotalPages: 0,
79+
Page: queryParams.Page,
80+
Limit: queryParams.Limit,
81+
TotalItems: 0, // TODO: Implement total items count
82+
TotalPages: 0, // TODO: Implement total pages count
10583
},
106-
Data: []interface{}{queryParams},
84+
Data: []interface{}{logs},
10785
Aggregations: map[string]interface{}{
108-
"count": 100,
109-
"sum_value": "1000000000000000000000",
110-
"avg_gas_price": "20000000000",
86+
"aggregates": queryParams.Aggregates,
11187
},
11288
}
11389

90+
sendJSONResponse(w, response)
91+
}
92+
93+
func getMainStorage() (storage.IMainStorage, error) {
94+
storageOnce.Do(func() {
95+
var err error
96+
mainStorage, err = storage.NewConnector[storage.IMainStorage](&config.Cfg.Storage.Main)
97+
if err != nil {
98+
storageErr = err
99+
log.Error().Err(err).Msg("Error creating storage connector")
100+
}
101+
})
102+
return mainStorage, storageErr
103+
}
104+
105+
func sendJSONResponse(w http.ResponseWriter, response interface{}) {
114106
w.Header().Set("Content-Type", "application/json")
115-
err = json.NewEncoder(w).Encode(response)
116-
if err != nil {
107+
if err := json.NewEncoder(w).Encode(response); err != nil {
117108
log.Error().Err(err).Msg("Error encoding response")
118109
api.InternalErrorHandler(w)
119-
return
120110
}
121111
}
+38-74
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,81 @@
11
package handlers
22

33
import (
4-
"encoding/json"
54
"net/http"
65

76
"github.com/go-chi/chi/v5"
87
"github.com/rs/zerolog/log"
98
"github.com/thirdweb-dev/indexer/api"
9+
"github.com/thirdweb-dev/indexer/internal/storage"
1010
)
1111

1212
func GetTransactions(w http.ResponseWriter, r *http.Request) {
13-
chainId, err := api.GetChainId(r)
14-
if err != nil {
15-
api.BadRequestErrorHandler(w, err)
16-
return
17-
}
18-
19-
queryParams, err := api.ParseQueryParams(r)
20-
if err != nil {
21-
api.BadRequestErrorHandler(w, err)
22-
return
23-
}
13+
handleTransactionsRequest(w, r, "", "")
14+
}
2415

25-
var response = api.QueryResponse{
26-
Meta: api.Meta{
27-
ChainIdentifier: chainId,
28-
ContractAddress: "todo",
29-
FunctionSig: "todo",
30-
Page: 1,
31-
Limit: 100,
32-
TotalItems: 0,
33-
TotalPages: 0,
34-
},
35-
Data: []interface{}{queryParams},
36-
}
16+
func GetTransactionsByContract(w http.ResponseWriter, r *http.Request) {
17+
contractAddress := chi.URLParam(r, "contractAddress")
18+
handleTransactionsRequest(w, r, contractAddress, "")
19+
}
3720

38-
w.Header().Set("Content-Type", "application/json")
39-
err = json.NewEncoder(w).Encode(response)
40-
if err != nil {
41-
log.Error().Err(err).Msg("Error encoding response")
42-
api.InternalErrorHandler(w)
43-
return
44-
}
21+
func GetTransactionsByContractAndSignature(w http.ResponseWriter, r *http.Request) {
22+
contractAddress := chi.URLParam(r, "contractAddress")
23+
functionSig := chi.URLParam(r, "functionSig")
24+
handleTransactionsRequest(w, r, contractAddress, functionSig)
4525
}
4626

47-
func GetTransactionsWithContract(w http.ResponseWriter, r *http.Request) {
27+
func handleTransactionsRequest(w http.ResponseWriter, r *http.Request, contractAddress, functionSig string) {
4828
chainId, err := api.GetChainId(r)
4929
if err != nil {
5030
api.BadRequestErrorHandler(w, err)
5131
return
5232
}
53-
contractAddress := chi.URLParam(r, "contractAddress")
33+
5434
queryParams, err := api.ParseQueryParams(r)
5535
if err != nil {
5636
api.BadRequestErrorHandler(w, err)
5737
return
5838
}
5939

60-
var response = api.QueryResponse{
61-
Meta: api.Meta{
62-
ChainIdentifier: chainId,
63-
ContractAddress: contractAddress,
64-
FunctionSig: "todo",
65-
Page: 1,
66-
Limit: 100,
67-
TotalItems: 0,
68-
TotalPages: 0,
69-
},
70-
Data: []interface{}{queryParams},
71-
}
72-
73-
w.Header().Set("Content-Type", "application/json")
74-
err = json.NewEncoder(w).Encode(response)
40+
mainStorage, err := getMainStorage()
7541
if err != nil {
76-
log.Error().Err(err).Msg("Error encoding response")
42+
log.Error().Err(err).Msg("Error creating storage connector")
7743
api.InternalErrorHandler(w)
7844
return
7945
}
8046

81-
}
82-
83-
func GetTransactionsWithContractAndSignature(w http.ResponseWriter, r *http.Request) {
84-
chainId, err := api.GetChainId(r)
85-
if err != nil {
86-
api.BadRequestErrorHandler(w, err)
87-
return
88-
}
89-
contractAddress := chi.URLParam(r, "contractAddress")
90-
functionSig := chi.URLParam(r, "functionSig")
91-
queryParams, err := api.ParseQueryParams(r)
47+
transactions, err := mainStorage.GetTransactions(storage.QueryFilter{
48+
FilterParams: queryParams.FilterParams,
49+
GroupBy: queryParams.GroupBy,
50+
SortBy: queryParams.SortBy,
51+
SortOrder: queryParams.SortOrder,
52+
Page: queryParams.Page,
53+
Limit: queryParams.Limit,
54+
Aggregates: queryParams.Aggregates,
55+
ContractAddress: contractAddress,
56+
FunctionSig: functionSig,
57+
})
9258
if err != nil {
93-
api.BadRequestErrorHandler(w, err)
59+
log.Error().Err(err).Msg("Error querying transactions")
60+
api.InternalErrorHandler(w)
9461
return
9562
}
9663

97-
var response = api.QueryResponse{
64+
response := api.QueryResponse{
9865
Meta: api.Meta{
9966
ChainIdentifier: chainId,
10067
ContractAddress: contractAddress,
10168
FunctionSig: functionSig,
102-
Page: 1,
103-
Limit: 100,
104-
TotalItems: 0,
105-
TotalPages: 0,
69+
Page: queryParams.Page,
70+
Limit: queryParams.Limit,
71+
TotalItems: 0, // TODO: Implement total items count
72+
TotalPages: 0, // TODO: Implement total pages count
73+
},
74+
Data: []interface{}{transactions},
75+
Aggregations: map[string]interface{}{
76+
"aggregates": queryParams.Aggregates,
10677
},
107-
Data: []interface{}{queryParams},
10878
}
10979

110-
w.Header().Set("Content-Type", "application/json")
111-
err = json.NewEncoder(w).Encode(response)
112-
if err != nil {
113-
log.Error().Err(err).Msg("Error encoding response")
114-
api.InternalErrorHandler(w)
115-
return
116-
}
80+
sendJSONResponse(w, response)
11781
}

0 commit comments

Comments
 (0)