feat(schema): indexing API (#20647)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package decoding
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"cosmossdk.io/schema"
|
||||
)
|
||||
|
||||
// DecoderResolver is an interface that allows indexers to discover and use module decoders.
|
||||
type DecoderResolver interface {
|
||||
// IterateAll iterates over all available module decoders.
|
||||
IterateAll(func(moduleName string, cdc schema.ModuleCodec) error) error
|
||||
|
||||
// LookupDecoder looks up a specific module decoder.
|
||||
LookupDecoder(moduleName string) (decoder schema.ModuleCodec, found bool, err error)
|
||||
}
|
||||
|
||||
// ModuleSetDecoderResolver returns DecoderResolver that will discover modules implementing
|
||||
// DecodeableModule in the provided module set.
|
||||
func ModuleSetDecoderResolver(moduleSet map[string]interface{}) DecoderResolver {
|
||||
return &moduleSetDecoderResolver{
|
||||
moduleSet: moduleSet,
|
||||
}
|
||||
}
|
||||
|
||||
type moduleSetDecoderResolver struct {
|
||||
moduleSet map[string]interface{}
|
||||
}
|
||||
|
||||
func (a moduleSetDecoderResolver) IterateAll(f func(string, schema.ModuleCodec) error) error {
|
||||
keys := make([]string, 0, len(a.moduleSet))
|
||||
for k := range a.moduleSet {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
module := a.moduleSet[k]
|
||||
dm, ok := module.(schema.HasModuleCodec)
|
||||
if ok {
|
||||
decoder, err := dm.ModuleCodec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f(k, decoder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a moduleSetDecoderResolver) LookupDecoder(moduleName string) (schema.ModuleCodec, bool, error) {
|
||||
mod, ok := a.moduleSet[moduleName]
|
||||
if !ok {
|
||||
return schema.ModuleCodec{}, false, nil
|
||||
}
|
||||
|
||||
dm, ok := mod.(schema.HasModuleCodec)
|
||||
if !ok {
|
||||
return schema.ModuleCodec{}, false, nil
|
||||
}
|
||||
|
||||
decoder, err := dm.ModuleCodec()
|
||||
return decoder, true, err
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package decoding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/schema"
|
||||
)
|
||||
|
||||
type modA struct{}
|
||||
|
||||
func (m modA) ModuleCodec() (schema.ModuleCodec, error) {
|
||||
return schema.ModuleCodec{
|
||||
Schema: schema.ModuleSchema{ObjectTypes: []schema.ObjectType{{Name: "A"}}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type modB struct{}
|
||||
|
||||
func (m modB) ModuleCodec() (schema.ModuleCodec, error) {
|
||||
return schema.ModuleCodec{
|
||||
Schema: schema.ModuleSchema{ObjectTypes: []schema.ObjectType{{Name: "B"}}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type modC struct{}
|
||||
|
||||
var moduleSet = map[string]interface{}{
|
||||
"modA": modA{},
|
||||
"modB": modB{},
|
||||
"modC": modC{},
|
||||
}
|
||||
|
||||
var resolver = ModuleSetDecoderResolver(moduleSet)
|
||||
|
||||
func TestModuleSetDecoderResolver_IterateAll(t *testing.T) {
|
||||
objectTypes := map[string]bool{}
|
||||
err := resolver.IterateAll(func(moduleName string, cdc schema.ModuleCodec) error {
|
||||
objectTypes[cdc.Schema.ObjectTypes[0].Name] = true
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(objectTypes) != 2 {
|
||||
t.Fatalf("expected 2 object types, got %d", len(objectTypes))
|
||||
}
|
||||
|
||||
if !objectTypes["A"] {
|
||||
t.Fatalf("expected object type A")
|
||||
}
|
||||
|
||||
if !objectTypes["B"] {
|
||||
t.Fatalf("expected object type B")
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleSetDecoderResolver_LookupDecoder(t *testing.T) {
|
||||
decoder, found, err := resolver.LookupDecoder("modA")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("expected to find decoder for modA")
|
||||
}
|
||||
|
||||
if decoder.Schema.ObjectTypes[0].Name != "A" {
|
||||
t.Fatalf("expected object type A, got %s", decoder.Schema.ObjectTypes[0].Name)
|
||||
}
|
||||
|
||||
decoder, found, err = resolver.LookupDecoder("modB")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("expected to find decoder for modB")
|
||||
}
|
||||
|
||||
if decoder.Schema.ObjectTypes[0].Name != "B" {
|
||||
t.Fatalf("expected object type B, got %s", decoder.Schema.ObjectTypes[0].Name)
|
||||
}
|
||||
|
||||
decoder, found, err = resolver.LookupDecoder("modC")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatalf("expected not to find decoder")
|
||||
}
|
||||
|
||||
decoder, found, err = resolver.LookupDecoder("modD")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if found {
|
||||
t.Fatalf("expected not to find decoder")
|
||||
}
|
||||
}
|
||||
|
||||
type modD struct{}
|
||||
|
||||
func (m modD) ModuleCodec() (schema.ModuleCodec, error) {
|
||||
return schema.ModuleCodec{}, fmt.Errorf("an error")
|
||||
}
|
||||
|
||||
func TestModuleSetDecoderResolver_IterateAll_Error(t *testing.T) {
|
||||
resolver := ModuleSetDecoderResolver(map[string]interface{}{
|
||||
"modD": modD{},
|
||||
})
|
||||
err := resolver.IterateAll(func(moduleName string, cdc schema.ModuleCodec) error {
|
||||
if moduleName == "modD" {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package decoding
|
||||
|
||||
// SyncSource is an interface that allows indexers to start indexing modules with pre-existing state.
|
||||
// It should generally be a wrapper around the key-value store.
|
||||
type SyncSource interface {
|
||||
// IterateAllKVPairs iterates over all key-value pairs for a given module.
|
||||
IterateAllKVPairs(moduleName string, fn func(key, value []byte) error) error
|
||||
}
|
||||
Reference in New Issue
Block a user