-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BCF-2684: copy types from core to support relayer implementations (#187)
- Loading branch information
Showing
26 changed files
with
920 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
golang 1.20.4 | ||
golang 1.21.1 | ||
golangci-lint 1.51.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
module github.com/smartcontractkit/chainlink-relay/ops | ||
|
||
go 1.20 | ||
go 1.21 | ||
|
||
require ( | ||
github.com/lib/pq v1.10.4 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package chains | ||
|
||
import ( | ||
"encoding/base64" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
|
||
"github.com/smartcontractkit/chainlink-relay/pkg/types" | ||
) | ||
|
||
// pageToken is simple internal representation for coordination requests and responses in a paginated API | ||
// It is inspired by the Google API Design patterns | ||
// https://cloud.google.com/apis/design/design_patterns#list_pagination | ||
// https://google.aip.dev/158 | ||
type pageToken struct { | ||
Page int | ||
Size int | ||
} | ||
|
||
var ( | ||
ErrInvalidToken = errors.New("invalid page token") | ||
ErrOutOfRange = errors.New("out of range") | ||
defaultSize = 100 | ||
) | ||
|
||
// Encode the token in base64 for transmission for the wire | ||
func (pr *pageToken) Encode() string { | ||
if pr.Size == 0 { | ||
pr.Size = defaultSize | ||
} | ||
// this is a simple minded implementation and may benefit from something fancier | ||
// note that this is a valid url.Query string, which we leverage in decoding | ||
s := fmt.Sprintf("page=%d&size=%d", pr.Page, pr.Size) | ||
return base64.RawStdEncoding.EncodeToString([]byte(s)) | ||
} | ||
|
||
// b64enc must be the base64 encoded token string, corresponding to [pageToken.Encode()] | ||
func NewPageToken(b64enc string) (*pageToken, error) { | ||
// empty is valid | ||
if b64enc == "" { | ||
return &pageToken{Page: 0, Size: defaultSize}, nil | ||
} | ||
|
||
b, err := base64.RawStdEncoding.DecodeString(b64enc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
// here too, this is simple minded and could be fancier | ||
|
||
vals, err := url.ParseQuery(string(b)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !(vals.Has("page") && vals.Has("size")) { | ||
return nil, ErrInvalidToken | ||
} | ||
page, err := strconv.Atoi(vals.Get("page")) | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: bad page", ErrInvalidToken) | ||
} | ||
size, err := strconv.Atoi(vals.Get("size")) | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: bad size", ErrInvalidToken) | ||
} | ||
return &pageToken{ | ||
Page: page, | ||
Size: size, | ||
}, err | ||
} | ||
|
||
// if start is out of range, must return ErrOutOfRange | ||
type ListNodeStatusFn = func(start, end int) (stats []types.NodeStatus, total int, err error) | ||
|
||
func ListNodeStatuses(pageSize int, pageTokenStr string, listFn ListNodeStatusFn) (stats []types.NodeStatus, nextPageToken string, total int, err error) { | ||
if pageSize == 0 { | ||
pageSize = defaultSize | ||
} | ||
t := &pageToken{Page: 0, Size: pageSize} | ||
if pageTokenStr != "" { | ||
t, err = NewPageToken(pageTokenStr) | ||
if err != nil { | ||
return nil, "", -1, err | ||
} | ||
} | ||
start, end := t.Page*t.Size, (t.Page+1)*t.Size | ||
stats, total, err = listFn(start, end) | ||
if err != nil { | ||
return stats, "", -1, err | ||
} | ||
if total > end { | ||
next_token := &pageToken{Page: t.Page + 1, Size: t.Size} | ||
nextPageToken = next_token.Encode() | ||
} | ||
return stats, nextPageToken, total, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package chains | ||
|
||
import ( | ||
"encoding/base64" | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/smartcontractkit/chainlink-relay/pkg/types" | ||
) | ||
|
||
func TestNewPageToken(t *testing.T) { | ||
type args struct { | ||
t *pageToken | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want *pageToken | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "empty", | ||
args: args{t: &pageToken{}}, | ||
want: &pageToken{Page: 0, Size: defaultSize}, | ||
}, | ||
{ | ||
name: "page set, size unset", | ||
args: args{t: &pageToken{Page: 1}}, | ||
want: &pageToken{Page: 1, Size: defaultSize}, | ||
}, | ||
{ | ||
name: "page set, size set", | ||
args: args{t: &pageToken{Page: 3, Size: 10}}, | ||
want: &pageToken{Page: 3, Size: 10}, | ||
}, | ||
{ | ||
name: "page unset, size set", | ||
args: args{t: &pageToken{Size: 17}}, | ||
want: &pageToken{Page: 0, Size: 17}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
enc := tt.args.t.Encode() | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := NewPageToken(enc) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("NewPageToken() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("NewPageToken() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestListNodeStatuses(t *testing.T) { | ||
testStats := []types.NodeStatus{ | ||
{ | ||
ChainID: "chain-1", | ||
Name: "name-1", | ||
}, | ||
{ | ||
ChainID: "chain-2", | ||
Name: "name-2", | ||
}, | ||
{ | ||
ChainID: "chain-3", | ||
Name: "name-3", | ||
}, | ||
} | ||
|
||
type args struct { | ||
pageSize int | ||
pageToken string | ||
listFn ListNodeStatusFn | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
wantStats []types.NodeStatus | ||
wantNextPageToken string | ||
wantTotal int | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "all on first page", | ||
args: args{ | ||
pageSize: 10, // > length of test stats | ||
pageToken: "", | ||
listFn: func(start, end int) ([]types.NodeStatus, int, error) { | ||
return testStats, len(testStats), nil | ||
}, | ||
}, | ||
wantNextPageToken: "", | ||
wantTotal: len(testStats), | ||
wantStats: testStats, | ||
}, | ||
{ | ||
name: "small first page", | ||
args: args{ | ||
pageSize: len(testStats) - 1, | ||
pageToken: "", | ||
listFn: func(start, end int) ([]types.NodeStatus, int, error) { | ||
return testStats[start:end], len(testStats), nil | ||
}, | ||
}, | ||
wantNextPageToken: base64.RawStdEncoding.EncodeToString([]byte("page=1&size=2")), // hard coded 2 is len(testStats)-1 | ||
wantTotal: len(testStats), | ||
wantStats: testStats[0 : len(testStats)-1], | ||
}, | ||
{ | ||
name: "second page", | ||
args: args{ | ||
pageSize: len(testStats) - 1, | ||
pageToken: base64.RawStdEncoding.EncodeToString([]byte("page=1&size=2")), // hard coded 2 is len(testStats)-1 | ||
listFn: func(start, end int) ([]types.NodeStatus, int, error) { | ||
// note list function must do the start, end bound checking. here we are making it simple | ||
if end > len(testStats) { | ||
end = len(testStats) | ||
} | ||
return testStats[start:end], len(testStats), nil | ||
}, | ||
}, | ||
wantNextPageToken: "", | ||
wantTotal: len(testStats), | ||
wantStats: testStats[len(testStats)-1:], | ||
}, | ||
{ | ||
name: "bad list fn", | ||
args: args{ | ||
listFn: func(start, end int) ([]types.NodeStatus, int, error) { | ||
return nil, 0, fmt.Errorf("i'm a bad list fn") | ||
}, | ||
}, | ||
wantTotal: -1, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "invalid token", | ||
args: args{ | ||
pageToken: "invalid token", | ||
listFn: func(start, end int) ([]types.NodeStatus, int, error) { | ||
return testStats[start:end], len(testStats), nil | ||
}, | ||
}, | ||
wantTotal: -1, | ||
wantErr: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
gotStats, gotNext_pageToken, gotTotal, err := ListNodeStatuses(tt.args.pageSize, tt.args.pageToken, tt.args.listFn) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("ListNodeStatuses() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
assert.Equal(t, tt.wantStats, gotStats) | ||
assert.Equal(t, tt.wantNextPageToken, gotNext_pageToken) | ||
assert.Equal(t, tt.wantTotal, gotTotal) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.