cosmos-sdk/orm/model/ormdb/module.go
Aaron Craelius d930b7b893
refactor(orm)!: update to new module schema descriptor (#11273)
## Description

Follow-up to #11119 to update the ORM code to use these definitions.



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-03-01 16:18:45 +00:00

172 lines
4.8 KiB
Go

package ormdb
import (
"bytes"
"context"
"encoding/binary"
"math"
"google.golang.org/protobuf/reflect/protoregistry"
ormv1alpha1 "github.com/cosmos/cosmos-sdk/api/cosmos/orm/v1alpha1"
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
"google.golang.org/protobuf/reflect/protodesc"
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
"github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
)
// ModuleDB defines the ORM database type to be used by modules.
type ModuleDB interface {
ormtable.Schema
// DefaultJSON writes default JSON for each table in the module to the target.
DefaultJSON(ormjson.WriteTarget) error
// ValidateJSON validates JSON for each table in the module.
ValidateJSON(ormjson.ReadSource) error
// ImportJSON imports JSON for each table in the module which has JSON
// defined in the read source.
ImportJSON(context.Context, ormjson.ReadSource) error
// ExportJSON exports JSON for each table in the module.
ExportJSON(context.Context, ormjson.WriteTarget) error
}
type moduleDB struct {
prefix []byte
filesById map[uint32]*fileDescriptorDB
tablesByName map[protoreflect.FullName]ormtable.Table
}
// ModuleDBOptions are options for constructing a ModuleDB.
type ModuleDBOptions struct {
// TypeResolver is an optional type resolver to be used when unmarshaling
// protobuf messages. If it is nil, protoregistry.GlobalTypes will be used.
TypeResolver ormtable.TypeResolver
// FileResolver is an optional file resolver that can be used to retrieve
// pinned file descriptors that may be different from those available at
// runtime. The file descriptor versions returned by this resolver will be
// used instead of the ones provided at runtime by the ModuleSchema.
FileResolver protodesc.Resolver
// JSONValidator is an optional validator that can be used for validating
// messaging when using ValidateJSON. If it is nil, DefaultJSONValidator
// will be used
JSONValidator func(proto.Message) error
// GetBackendResolver returns a backend resolver for the requested storage
// type or an error if this type of storage isn't supported.
GetBackendResolver func(ormv1alpha1.StorageType) (ormtable.BackendResolver, error)
}
// NewModuleDB constructs a ModuleDB instance from the provided schema and options.
func NewModuleDB(schema *ormv1alpha1.ModuleSchemaDescriptor, options ModuleDBOptions) (ModuleDB, error) {
prefix := schema.Prefix
db := &moduleDB{
prefix: prefix,
filesById: map[uint32]*fileDescriptorDB{},
tablesByName: map[protoreflect.FullName]ormtable.Table{},
}
fileResolver := options.FileResolver
if fileResolver == nil {
fileResolver = protoregistry.GlobalFiles
}
for _, entry := range schema.SchemaFile {
var backendResolver ormtable.BackendResolver
var err error
if options.GetBackendResolver != nil {
backendResolver, err = options.GetBackendResolver(entry.StorageType)
if err != nil {
return nil, err
}
}
id := entry.Id
fileDescriptor, err := fileResolver.FindFileByPath(entry.ProtoFileName)
if err != nil {
return nil, err
}
if id == 0 {
return nil, ormerrors.InvalidFileDescriptorID.Wrapf("for %s", fileDescriptor.Path())
}
opts := fileDescriptorDBOptions{
ID: id,
Prefix: prefix,
TypeResolver: options.TypeResolver,
JSONValidator: options.JSONValidator,
BackendResolver: backendResolver,
}
fdSchema, err := newFileDescriptorDB(fileDescriptor, opts)
if err != nil {
return nil, err
}
db.filesById[id] = fdSchema
for name, table := range fdSchema.tablesByName {
if _, ok := db.tablesByName[name]; ok {
return nil, ormerrors.UnexpectedError.Wrapf("duplicate table %s", name)
}
db.tablesByName[name] = table
}
}
return db, nil
}
func (m moduleDB) DecodeEntry(k, v []byte) (ormkv.Entry, error) {
r := bytes.NewReader(k)
err := encodeutil.SkipPrefix(r, m.prefix)
if err != nil {
return nil, err
}
id, err := binary.ReadUvarint(r)
if err != nil {
return nil, err
}
if id > math.MaxUint32 {
return nil, ormerrors.UnexpectedDecodePrefix.Wrapf("uint32 varint id out of range %d", id)
}
fileSchema, ok := m.filesById[uint32(id)]
if !ok {
return nil, ormerrors.UnexpectedDecodePrefix.Wrapf("can't find FileDescriptor schema with id %d", id)
}
return fileSchema.DecodeEntry(k, v)
}
func (m moduleDB) EncodeEntry(entry ormkv.Entry) (k, v []byte, err error) {
tableName := entry.GetTableName()
table, ok := m.tablesByName[tableName]
if !ok {
return nil, nil, ormerrors.BadDecodeEntry.Wrapf("can't find table %s", tableName)
}
return table.EncodeEntry(entry)
}
func (m moduleDB) GetTable(message proto.Message) ormtable.Table {
return m.tablesByName[message.ProtoReflect().Descriptor().FullName()]
}