diff --git a/backend/pkg/api/api_test.go b/backend/pkg/api/api_test.go index 8d5423fa5..11a4c4e07 100644 --- a/backend/pkg/api/api_test.go +++ b/backend/pkg/api/api_test.go @@ -298,8 +298,9 @@ func TestInternalSearchHandler(t *testing.T) { }`)).Expect().Status(http.StatusOK).JSON().Decode(&resp) assert.NotEqual(t, 0, len(resp.Data), "response data should not be empty") - assert.NotNil(t, resp.Data[0].NumValue, "validator index should not be nil") - assert.Equal(t, uint64(5), *resp.Data[0].NumValue, "validator index should be 5") + validatorByIndex, ok := resp.Data[0].Value.(api_types.SearchValidator) + assert.True(t, ok, "response data should be of type SearchValidator") + assert.Equal(t, uint64(5), validatorByIndex.Index, "validator index should be 5") // search for validator by pubkey resp = api_types.InternalPostSearchResponse{} @@ -324,8 +325,9 @@ func TestInternalSearchHandler(t *testing.T) { }`)).Expect().Status(http.StatusOK).JSON().Decode(&resp) assert.NotEqual(t, 0, len(resp.Data), "response data should not be empty") - assert.NotNil(t, resp.Data[0].NumValue, "validator index should not be nil") - assert.Equal(t, uint64(5), *resp.Data[0].NumValue, "validator index should be 5") + validatorByPublicKey, ok := resp.Data[0].Value.(api_types.SearchValidator) + assert.True(t, ok, "response data should be of type SearchValidator") + assert.Equal(t, uint64(5), validatorByPublicKey.Index, "validator index should be 5") // search for validator by withdawal address resp = api_types.InternalPostSearchResponse{} @@ -349,8 +351,9 @@ func TestInternalSearchHandler(t *testing.T) { }`)).Expect().Status(http.StatusOK).JSON().Decode(&resp) assert.NotEqual(t, 0, len(resp.Data), "response data should not be empty") - assert.NotNil(t, resp.Data[0].NumValue, "validator index should not be nil") - assert.Greater(t, *resp.Data[0].NumValue, uint64(0), "returned number of validators should be greater than 0") + validatorsByWithdrawalAddress, ok := resp.Data[0].Value.(api_types.SearchValidatorsByWithdrwalCredential) + assert.True(t, ok, "response data should be of type SearchValidator") + assert.Greater(t, validatorsByWithdrawalAddress.Count, uint64(0), "returned number of validators should be greater than 0") } func TestPublicAndSharedDashboards(t *testing.T) { diff --git a/backend/pkg/api/data_access/dummy.go b/backend/pkg/api/data_access/dummy.go index ebb280b1e..84915194f 100644 --- a/backend/pkg/api/data_access/dummy.go +++ b/backend/pkg/api/data_access/dummy.go @@ -514,16 +514,16 @@ func (d *DummyService) GetSearchValidatorsByDepositAddress(ctx context.Context, return getDummyStruct[t.SearchValidatorsByDepositAddress](ctx) } -func (d *DummyService) GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositEnsName, error) { - return getDummyStruct[t.SearchValidatorsByDepositEnsName](ctx) +func (d *DummyService) GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositAddress, error) { + return getDummyStruct[t.SearchValidatorsByDepositAddress](ctx) } func (d *DummyService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error) { return getDummyStruct[t.SearchValidatorsByWithdrwalCredential](ctx) } -func (d *DummyService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithrawalEnsName, error) { - return getDummyStruct[t.SearchValidatorsByWithrawalEnsName](ctx) +func (d *DummyService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error) { + return getDummyStruct[t.SearchValidatorsByWithdrwalCredential](ctx) } func (d *DummyService) GetSearchValidatorsByGraffiti(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) { diff --git a/backend/pkg/api/data_access/search.go b/backend/pkg/api/data_access/search.go index 57221fbda..70c4c0536 100644 --- a/backend/pkg/api/data_access/search.go +++ b/backend/pkg/api/data_access/search.go @@ -12,9 +12,9 @@ type SearchRepository interface { GetSearchValidatorByIndex(ctx context.Context, chainId, index uint64) (*t.SearchValidator, error) GetSearchValidatorByPublicKey(ctx context.Context, chainId uint64, publicKey []byte) (*t.SearchValidator, error) GetSearchValidatorsByDepositAddress(ctx context.Context, chainId uint64, address []byte) (*t.SearchValidatorsByDepositAddress, error) - GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositEnsName, error) + GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositAddress, error) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error) - GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithrawalEnsName, error) + GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error) GetSearchValidatorsByGraffiti(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) } @@ -28,7 +28,7 @@ func (d *DataAccessService) GetSearchValidatorByIndex(ctx context.Context, chain if int(index) < len(validatorMapping.ValidatorPubkeys) { return &t.SearchValidator{ Index: index, - PublicKey: hexutil.MustDecode(validatorMapping.ValidatorPubkeys[index]), + PublicKey: validatorMapping.ValidatorPubkeys[index], }, nil } @@ -46,7 +46,7 @@ func (d *DataAccessService) GetSearchValidatorByPublicKey(ctx context.Context, c if index, found := validatorMapping.ValidatorIndices[b]; found { return &t.SearchValidator{ Index: index, - PublicKey: publicKey, + PublicKey: b, }, nil } @@ -56,9 +56,10 @@ func (d *DataAccessService) GetSearchValidatorByPublicKey(ctx context.Context, c func (d *DataAccessService) GetSearchValidatorsByDepositAddress(ctx context.Context, chainId uint64, address []byte) (*t.SearchValidatorsByDepositAddress, error) { // TODO: implement handling of chainid ret := &t.SearchValidatorsByDepositAddress{ - Address: address, + Address: hexutil.Encode(address), } - err := db.ReaderDb.GetContext(ctx, &ret.Count, "select count(validatorindex) from validators where pubkey in (select publickey from eth1_deposits where from_address = $1);", address) + err := db.ReaderDb.GetContext(ctx, &ret.Count, ` + select count(validatorindex) from validators where pubkey in (select publickey from eth1_deposits where from_address = $1);`, address) if err != nil { return nil, err } @@ -68,7 +69,7 @@ func (d *DataAccessService) GetSearchValidatorsByDepositAddress(ctx context.Cont return ret, nil } -func (d *DataAccessService) GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositEnsName, error) { +func (d *DataAccessService) GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositAddress, error) { // TODO: implement handling of chainid // TODO: finalize ens implementation first return nil, ErrNotFound @@ -77,7 +78,7 @@ func (d *DataAccessService) GetSearchValidatorsByDepositEnsName(ctx context.Cont func (d *DataAccessService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error) { // TODO: implement handling of chainid ret := &t.SearchValidatorsByWithdrwalCredential{ - WithdrawalCredential: credential, + WithdrawalCredential: hexutil.Encode(credential), } err := db.ReaderDb.GetContext(ctx, &ret.Count, "select count(validatorindex) from validators where withdrawalcredentials = $1;", credential) if err != nil { @@ -89,7 +90,7 @@ func (d *DataAccessService) GetSearchValidatorsByWithdrawalCredential(ctx contex return ret, nil } -func (d *DataAccessService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithrawalEnsName, error) { +func (d *DataAccessService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error) { // TODO: implement handling of chainid // TODO: finalize ens implementation first return nil, ErrNotFound diff --git a/backend/pkg/api/handlers/search.go b/backend/pkg/api/handlers/search.go index 531c0f898..e1a9f28c6 100644 --- a/backend/pkg/api/handlers/search.go +++ b/backend/pkg/api/handlers/search.go @@ -33,16 +33,48 @@ const ( ) // source of truth for all possible search types and their regex -var searchTypeToRegex = map[searchTypeKey]*regexp.Regexp{ - validatorByIndex: reInteger, - validatorByPublicKey: reValidatorPublicKey, - validatorList: reValidatorList, - validatorsByDepositAddress: reEthereumAddress, - validatorsByDepositEnsName: reEnsName, - validatorsByWithdrawalCredential: reWithdrawalCredential, - validatorsByWithdrawalAddress: reEthereumAddress, - validatorsByWithdrawalEns: reEnsName, - validatorsByGraffiti: reGraffiti, +var searchTypeMap = map[searchTypeKey]searchType{ + validatorByIndex: { + regex: reInteger, + responseType: "validator", + }, + validatorByPublicKey: { + regex: reValidatorPublicKey, + responseType: "validator", + }, + validatorList: { + regex: reValidatorList, + responseType: string(validatorList), + }, + validatorsByDepositAddress: { + regex: reEthereumAddress, + responseType: string(validatorsByDepositAddress), + }, + validatorsByDepositEnsName: { + regex: reEnsName, + responseType: string(validatorsByDepositAddress), + }, + validatorsByWithdrawalCredential: { + regex: reWithdrawalCredential, + responseType: string(validatorsByWithdrawalCredential), + }, + validatorsByWithdrawalAddress: { + regex: reEthereumAddress, + responseType: string(validatorsByWithdrawalCredential), + }, + validatorsByWithdrawalEns: { + regex: reEnsName, + responseType: string(validatorsByWithdrawalCredential), + }, + validatorsByGraffiti: { + regex: reGraffiti, + responseType: string(validatorsByGraffiti), + }, +} + +type searchType struct { + regex *regexp.Regexp + responseType string } // -------------------------------------- @@ -74,7 +106,7 @@ func (h *HandlerService) InternalPostSearch(w http.ResponseWriter, r *http.Reque // iterate over all combinations of search types and networks for _, searchType := range searchTypeSet { // check if input matches the regex for the search type - if !searchTypeToRegex[searchType].MatchString(req.Input) { + if !searchTypeMap[searchType].regex.MatchString(req.Input) { continue } for _, chainId := range chainIdSet { @@ -146,6 +178,17 @@ func (h *HandlerService) handleSearchType(ctx context.Context, input string, sea } } +func asSearchResult[In any](searchType searchTypeKey, chainId uint64, result *In, err error) (*types.SearchResult, error) { + if err != nil || result == nil { + return nil, err + } + return &types.SearchResult{ + Type: searchTypeMap[searchType].responseType, + ChainId: chainId, + Value: result, + }, nil +} + func (h *HandlerService) handleSearchValidatorByIndex(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { index, err := strconv.ParseUint(input, 10, 64) if err != nil { @@ -153,16 +196,7 @@ func (h *HandlerService) handleSearchValidatorByIndex(ctx context.Context, input return nil, err } result, err := h.daService.GetSearchValidatorByIndex(ctx, chainId, index) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorByIndex), - ChainId: chainId, - HashValue: "0x" + hex.EncodeToString(result.PublicKey), - NumValue: &result.Index, - }, nil + return asSearchResult(validatorByIndex, chainId, result, err) } func (h *HandlerService) handleSearchValidatorByPublicKey(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { @@ -172,16 +206,7 @@ func (h *HandlerService) handleSearchValidatorByPublicKey(ctx context.Context, i return nil, err } result, err := h.daService.GetSearchValidatorByPublicKey(ctx, chainId, publicKey) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorByPublicKey), - ChainId: chainId, - HashValue: "0x" + hex.EncodeToString(result.PublicKey), - NumValue: &result.Index, - }, nil + return asSearchResult(validatorByPublicKey, chainId, result, err) } func (h *HandlerService) handleSearchValidatorList(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { @@ -192,18 +217,14 @@ func (h *HandlerService) handleSearchValidatorList(ctx context.Context, input st return nil, nil // return no error as to not disturb the other search types } validators, err := h.daService.GetValidatorsFromSlices(ctx, indices, pubkeys) - if err != nil { + if err != nil || validators == nil || len(validators) == 0 { return nil, err } - if len(validators) == 0 { - return nil, nil - } - var resultLength uint64 = uint64(len(validators)) return &types.SearchResult{ - Type: string(validatorList), - ChainId: chainId, - NumValue: &resultLength, + Type: searchTypeMap[validatorList].responseType, + ChainId: chainId, + Value: types.SearchValidatorList{Validators: validators}, }, nil } @@ -213,31 +234,12 @@ func (h *HandlerService) handleSearchValidatorsByDepositAddress(ctx context.Cont return nil, err } result, err := h.daService.GetSearchValidatorsByDepositAddress(ctx, chainId, address) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByDepositAddress), - ChainId: chainId, - HashValue: "0x" + hex.EncodeToString(result.Address), - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByDepositAddress, chainId, result, err) } func (h *HandlerService) handleSearchValidatorsByDepositEnsName(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { result, err := h.daService.GetSearchValidatorsByDepositEnsName(ctx, chainId, input) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByDepositEnsName), - ChainId: chainId, - StrValue: result.EnsName, - HashValue: "0x" + hex.EncodeToString(result.Address), - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByDepositEnsName, chainId, result, err) } func (h *HandlerService) handleSearchValidatorsByWithdrawalCredential(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { @@ -246,16 +248,7 @@ func (h *HandlerService) handleSearchValidatorsByWithdrawalCredential(ctx contex return nil, err } result, err := h.daService.GetSearchValidatorsByWithdrawalCredential(ctx, chainId, withdrawalCredential) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByWithdrawalCredential), - ChainId: chainId, - HashValue: "0x" + hex.EncodeToString(result.WithdrawalCredential), - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByWithdrawalCredential, chainId, result, err) } func (h *HandlerService) handleSearchValidatorsByWithdrawalAddress(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { @@ -265,45 +258,17 @@ func (h *HandlerService) handleSearchValidatorsByWithdrawalAddress(ctx context.C return nil, err } result, err := h.daService.GetSearchValidatorsByWithdrawalCredential(ctx, chainId, withdrawalCredential) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByWithdrawalAddress), - ChainId: chainId, - HashValue: "0x" + hex.EncodeToString(result.WithdrawalCredential), - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByWithdrawalAddress, chainId, result, err) } func (h *HandlerService) handleSearchValidatorsByWithdrawalEnsName(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { result, err := h.daService.GetSearchValidatorsByWithdrawalEnsName(ctx, chainId, input) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByWithdrawalEns), - ChainId: chainId, - StrValue: result.EnsName, - HashValue: "0x" + hex.EncodeToString(result.Address), - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByWithdrawalEns, chainId, result, err) } func (h *HandlerService) handleSearchValidatorsByGraffiti(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) { result, err := h.daService.GetSearchValidatorsByGraffiti(ctx, chainId, input) - if err != nil { - return nil, err - } - - return &types.SearchResult{ - Type: string(validatorsByGraffiti), - ChainId: chainId, - StrValue: result.Graffiti, - NumValue: &result.Count, - }, nil + return asSearchResult(validatorsByGraffiti, chainId, result, err) } // -------------------------------------- @@ -336,14 +301,14 @@ func (v *validationError) checkSearchTypes(types []searchTypeKey) []searchTypeKe typeSet := map[searchTypeKey]struct{}{} // if the list is empty, query all types if len(types) == 0 { - for t := range searchTypeToRegex { + for t := range searchTypeMap { typeSet[t] = struct{}{} } return slices.Collect(maps.Keys(typeSet)) } // list not empty, check if types are valid for _, t := range types { - if _, typeExists := searchTypeToRegex[t]; !typeExists { + if _, typeExists := searchTypeMap[t]; !typeExists { v.add("types", fmt.Sprintf("invalid search type '%s'", t)) continue } diff --git a/backend/pkg/api/types/common.go b/backend/pkg/api/types/common.go index 047cadac6..e64c29cc1 100644 --- a/backend/pkg/api/types/common.go +++ b/backend/pkg/api/types/common.go @@ -113,16 +113,6 @@ type ChainConfig struct { // TODO: add more fields, depending on what frontend needs } -type SearchResult struct { - Type string `json:"type"` - ChainId uint64 `json:"chain_id"` - HashValue string `json:"hash_value,omitempty"` - StrValue string `json:"str_value,omitempty"` - NumValue *uint64 `json:"num_value,omitempty"` -} - -type InternalPostSearchResponse ApiDataResponse[[]SearchResult] - type VDBPublicId struct { PublicId string `json:"public_id"` DashboardId int `json:"-"` diff --git a/backend/pkg/api/types/search.go b/backend/pkg/api/types/search.go index c289d0e82..ab2fc812e 100644 --- a/backend/pkg/api/types/search.go +++ b/backend/pkg/api/types/search.go @@ -3,33 +3,38 @@ package types // search types to be used between the data access layer and the api layer, shouldn't be exported to typescript type SearchValidator struct { - Index uint64 - PublicKey []byte + Index uint64 `json:"index"` + PublicKey string `json:"public_key"` } -type SearchValidatorsByDepositEnsName struct { - EnsName string - Address []byte - Count uint64 +type SearchValidatorList struct { + Validators []uint64 `json:"validators"` + Count uint64 `json:"count"` } type SearchValidatorsByDepositAddress struct { - Address []byte - Count uint64 + EnsName string `json:"ens_name,omitempty"` + Address string `json:"address"` + Count uint64 `json:"count"` } type SearchValidatorsByWithdrwalCredential struct { - WithdrawalCredential []byte - Count uint64 + EnsName string `json:"ens_name,omitempty"` + WithdrawalCredential string `json:"withdrawal_credential"` + Count uint64 `json:"count"` } -type SearchValidatorsByWithrawalEnsName struct { - EnsName string - Address []byte - Count uint64 +type SearchValidatorsByGraffiti struct { + Graffiti string `json:"graffiti"` + Count uint64 `json:"count"` } -type SearchValidatorsByGraffiti struct { - Graffiti string - Count uint64 +type SearchResult struct { + Type string `json:"type"` + ChainId uint64 `json:"chain_id"` + Value interface{} `json:"value"` +} + +type InternalPostSearchResponse struct { + Data []SearchResult `json:"data" tstype:"{ type: 'validator_by_index'; value: SearchValidator } | { type: 'validator_by_public_key'; value: SearchValidator } |{ type: 'validator_list'; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_deposit_ens_name'; value: SearchValidatorsByDepositEnsName } | { type: 'validators_by_withdrawal_address'; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_withdrawal_credential'; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_withdrawal_ens_name'; value: SearchValidatorsByWithrawalEnsName } | { type: 'validators_by_graffiti'; value: SearchValidatorsByGraffiti }[]"` } diff --git a/frontend/types/api/common.ts b/frontend/types/api/common.ts index 992f10f01..64c633d22 100644 --- a/frontend/types/api/common.ts +++ b/frontend/types/api/common.ts @@ -92,14 +92,6 @@ export interface ChainConfig { chain_id: number /* uint64 */; name: string; } -export interface SearchResult { - type: string; - chain_id: number /* uint64 */; - hash_value?: string; - str_value?: string; - num_value?: number /* uint64 */; -} -export type InternalPostSearchResponse = ApiDataResponse; export interface VDBPublicId { public_id: string; name?: string; diff --git a/frontend/types/api/search.ts b/frontend/types/api/search.ts new file mode 100644 index 000000000..dc597cf83 --- /dev/null +++ b/frontend/types/api/search.ts @@ -0,0 +1,37 @@ +// Code generated by tygo. DO NOT EDIT. +/* eslint-disable */ +import type { Address } from './common' + +////////// +// source: search.go + +export interface SearchValidator { + index: number /* uint64 */; + public_key: string; +} +export interface SearchValidatorList { + validators: number /* uint64 */[]; + count: number /* uint64 */; +} +export interface SearchValidatorsByDepositAddress { + ens_name?: string; + address: string; + count: number /* uint64 */; +} +export interface SearchValidatorsByWithdrwalCredential { + ens_name?: string; + withdrawal_credential: string; + count: number /* uint64 */; +} +export interface SearchValidatorsByGraffiti { + graffiti: string; + count: number /* uint64 */; +} +export interface SearchResult { + type: string; + chain_id: number /* uint64 */; + value: any; +} +export interface InternalPostSearchResponse { + data: { type: 'validator_by_index'; value: SearchValidator } | { type: 'validator_by_public_key'; value: SearchValidator } |{ type: 'validator_list'; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_deposit_ens_name'; value: SearchValidatorsByDepositEnsName } | { type: 'validators_by_withdrawal_address'; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_withdrawal_credential'; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_withdrawal_ens_name'; value: SearchValidatorsByWithrawalEnsName } | { type: 'validators_by_graffiti'; value: SearchValidatorsByGraffiti }[]; +}