refactor(orm)!: support core genesis (#14822)
This commit is contained in:
parent
364d3af5e7
commit
85e5896e78
41
orm/CHANGELOG.md
Normal file
41
orm/CHANGELOG.md
Normal file
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
Guiding Principles:
|
||||
|
||||
Changelogs are for humans, not machines.
|
||||
There should be an entry for every single version.
|
||||
The same types of changes should be grouped.
|
||||
Versions and sections should be linkable.
|
||||
The latest version comes first.
|
||||
The release date of each version is displayed.
|
||||
Mention whether you follow Semantic Versioning.
|
||||
|
||||
Usage:
|
||||
|
||||
Change log entries are to be added to the Unreleased section under the
|
||||
appropriate stanza (see below). Each entry should ideally include a tag and
|
||||
the Github issue reference in the following format:
|
||||
|
||||
* (<tag>) \#<issue-number> message
|
||||
|
||||
The issue numbers will later be link-ified during the release process so you do
|
||||
not have to worry about including a link manually, but you can if you wish.
|
||||
|
||||
Types of changes (Stanzas):
|
||||
|
||||
"Features" for new features.
|
||||
"Improvements" for changes in existing functionality.
|
||||
"Deprecated" for soon-to-be removed features.
|
||||
"Bug Fixes" for any bug fixes.
|
||||
"Client Breaking" for breaking Protobuf, gRPC and REST routes used by end-users.
|
||||
"CLI Breaking" for breaking CLI commands.
|
||||
"API Breaking" for breaking exported APIs used by developers building on SDK.
|
||||
Ref: https://keepachangelog.com/en/1.0.0/
|
||||
-->
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### API-Breaking Changes
|
||||
|
||||
- [14822](https://github.com/cosmos/cosmos-sdk/pull/14822) Migrate to cosmossdk.io/core genesis API
|
||||
@ -5,15 +5,24 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"golang.org/x/exp/maps"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
|
||||
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
|
||||
)
|
||||
|
||||
func (m moduleDB) DefaultJSON(target ormjson.WriteTarget) error {
|
||||
type appModuleGenesisWrapper struct {
|
||||
moduleDB
|
||||
}
|
||||
|
||||
func (m appModuleGenesisWrapper) IsOnePerModuleType() {}
|
||||
|
||||
func (m appModuleGenesisWrapper) IsAppModule() {}
|
||||
|
||||
func (m appModuleGenesisWrapper) DefaultGenesis(target appmodule.GenesisTarget) error {
|
||||
tableNames := maps.Keys(m.tablesByName)
|
||||
sort.Slice(tableNames, func(i, j int) bool {
|
||||
ti, tj := tableNames[i], tableNames[j]
|
||||
@ -22,7 +31,7 @@ func (m moduleDB) DefaultJSON(target ormjson.WriteTarget) error {
|
||||
|
||||
for _, name := range tableNames {
|
||||
table := m.tablesByName[name]
|
||||
w, err := target.OpenWriter(name)
|
||||
w, err := target(string(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -40,7 +49,7 @@ func (m moduleDB) DefaultJSON(target ormjson.WriteTarget) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m moduleDB) ValidateJSON(source ormjson.ReadSource) error {
|
||||
func (m appModuleGenesisWrapper) ValidateGenesis(source appmodule.GenesisSource) error {
|
||||
errMap := map[protoreflect.FullName]error{}
|
||||
names := maps.Keys(m.tablesByName)
|
||||
sort.Slice(names, func(i, j int) bool {
|
||||
@ -48,7 +57,7 @@ func (m moduleDB) ValidateJSON(source ormjson.ReadSource) error {
|
||||
return ti.Name() < tj.Name()
|
||||
})
|
||||
for _, name := range names {
|
||||
r, err := source.OpenReader(name)
|
||||
r, err := source(string(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -80,7 +89,7 @@ func (m moduleDB) ValidateJSON(source ormjson.ReadSource) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m moduleDB) ImportJSON(ctx context.Context, source ormjson.ReadSource) error {
|
||||
func (m appModuleGenesisWrapper) InitGenesis(ctx context.Context, source appmodule.GenesisSource) error {
|
||||
var names []string
|
||||
for name := range m.tablesByName {
|
||||
names = append(names, string(name))
|
||||
@ -91,7 +100,7 @@ func (m moduleDB) ImportJSON(ctx context.Context, source ormjson.ReadSource) err
|
||||
fullName := protoreflect.FullName(name)
|
||||
table := m.tablesByName[fullName]
|
||||
|
||||
r, err := source.OpenReader(fullName)
|
||||
r, err := source(string(fullName))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "table %s", fullName)
|
||||
}
|
||||
@ -114,7 +123,7 @@ func (m moduleDB) ImportJSON(ctx context.Context, source ormjson.ReadSource) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m moduleDB) ExportJSON(ctx context.Context, sink ormjson.WriteTarget) error {
|
||||
func (m appModuleGenesisWrapper) ExportGenesis(ctx context.Context, sink appmodule.GenesisTarget) error {
|
||||
// Ensure that we export the tables in a deterministic order.
|
||||
tableNames := maps.Keys(m.tablesByName)
|
||||
sort.Slice(tableNames, func(i, j int) bool {
|
||||
@ -123,7 +132,7 @@ func (m moduleDB) ExportJSON(ctx context.Context, sink ormjson.WriteTarget) erro
|
||||
})
|
||||
|
||||
for _, name := range tableNames {
|
||||
w, err := sink.OpenWriter(name)
|
||||
w, err := sink(string(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -7,13 +7,12 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"cosmossdk.io/core/store"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
|
||||
ormv1alpha1 "cosmossdk.io/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"
|
||||
@ -31,18 +30,19 @@ import (
|
||||
type ModuleDB interface {
|
||||
ormtable.Schema
|
||||
|
||||
// DefaultJSON writes default JSON for each table in the module to the target.
|
||||
DefaultJSON(ormjson.WriteTarget) error
|
||||
// GenesisHandler returns an implementation of appmodule.HasGenesis
|
||||
// to be embedded in or called from app module implementations.
|
||||
// Ex:
|
||||
// type Keeper struct {
|
||||
// appmodule.HasGenesis
|
||||
// }
|
||||
//
|
||||
// func NewKeeper(db ModuleDB) *Keeper {
|
||||
// return &Keeper{HasGenesis: db.GenesisHandler()}
|
||||
// }
|
||||
GenesisHandler() appmodule.HasGenesis
|
||||
|
||||
// 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
|
||||
private()
|
||||
}
|
||||
|
||||
type moduleDB struct {
|
||||
@ -212,3 +212,8 @@ func (m moduleDB) EncodeEntry(entry ormkv.Entry) (k, v []byte, err error) {
|
||||
func (m moduleDB) GetTable(message proto.Message) ormtable.Table {
|
||||
return m.tablesByName[message.ProtoReflect().Descriptor().FullName()]
|
||||
}
|
||||
func (m moduleDB) GenesisHandler() appmodule.HasGenesis {
|
||||
return appModuleGenesisWrapper{m}
|
||||
}
|
||||
|
||||
func (moduleDB) private() {}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
ormv1alpha1 "cosmossdk.io/api/cosmos/orm/v1alpha1"
|
||||
"cosmossdk.io/core/appconfig"
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"cosmossdk.io/core/genesis"
|
||||
"cosmossdk.io/core/store"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
|
||||
@ -32,7 +33,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
|
||||
"github.com/cosmos/cosmos-sdk/orm/testing/ormtest"
|
||||
"github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
|
||||
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
|
||||
)
|
||||
|
||||
// These tests use a simulated bank keeper. Addresses and balances use
|
||||
@ -225,40 +225,40 @@ func TestModuleDB(t *testing.T) {
|
||||
}
|
||||
|
||||
// check JSON
|
||||
target := ormjson.NewRawMessageTarget()
|
||||
assert.NilError(t, db.DefaultJSON(target))
|
||||
target := genesis.RawJSONTarget{}
|
||||
assert.NilError(t, db.GenesisHandler().DefaultGenesis(target.Target()))
|
||||
rawJson, err := target.JSON()
|
||||
assert.NilError(t, err)
|
||||
golden.Assert(t, string(rawJson), "default_json.golden")
|
||||
|
||||
target = ormjson.NewRawMessageTarget()
|
||||
assert.NilError(t, db.ExportJSON(ctx, target))
|
||||
target = genesis.RawJSONTarget{}
|
||||
assert.NilError(t, db.GenesisHandler().ExportGenesis(ctx, target.Target()))
|
||||
rawJson, err = target.JSON()
|
||||
assert.NilError(t, err)
|
||||
|
||||
goodJSON := `{
|
||||
"testpb.Supply": []
|
||||
}`
|
||||
source, err := ormjson.NewRawMessageSource(json.RawMessage(goodJSON))
|
||||
source, err := genesis.SourceFromRawJSON(json.RawMessage(goodJSON))
|
||||
assert.NilError(t, err)
|
||||
assert.NilError(t, db.ValidateJSON(source))
|
||||
assert.NilError(t, db.ImportJSON(ormtable.WrapContextDefault(ormtest.NewMemoryBackend()), source))
|
||||
assert.NilError(t, db.GenesisHandler().ValidateGenesis(source))
|
||||
assert.NilError(t, db.GenesisHandler().InitGenesis(ormtable.WrapContextDefault(ormtest.NewMemoryBackend()), source))
|
||||
|
||||
badJSON := `{
|
||||
"testpb.Balance": 5,
|
||||
"testpb.Supply": {}
|
||||
}
|
||||
`
|
||||
source, err = ormjson.NewRawMessageSource(json.RawMessage(badJSON))
|
||||
source, err = genesis.SourceFromRawJSON(json.RawMessage(badJSON))
|
||||
assert.NilError(t, err)
|
||||
assert.ErrorIs(t, db.ValidateJSON(source), ormerrors.JSONValidationError)
|
||||
assert.ErrorIs(t, db.GenesisHandler().ValidateGenesis(source), ormerrors.JSONValidationError)
|
||||
|
||||
backend2 := ormtest.NewMemoryBackend()
|
||||
ctx2 := ormtable.WrapContextDefault(backend2)
|
||||
source, err = ormjson.NewRawMessageSource(rawJson)
|
||||
source, err = genesis.SourceFromRawJSON(rawJson)
|
||||
assert.NilError(t, err)
|
||||
assert.NilError(t, db.ValidateJSON(source))
|
||||
assert.NilError(t, db.ImportJSON(ctx2, source))
|
||||
assert.NilError(t, db.GenesisHandler().ValidateGenesis(source))
|
||||
assert.NilError(t, db.GenesisHandler().InitGenesis(ctx2, source))
|
||||
testkv.AssertBackendsEqual(t, backend, backend2)
|
||||
}
|
||||
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
package ormjson
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
// ReadSource is a source for reading tables in JSON format. It
|
||||
// may abstract over a single JSON object or JSON in separate files that
|
||||
// can be streamed over.
|
||||
type ReadSource interface {
|
||||
// OpenReader returns an io.ReadCloser for the named table. If there
|
||||
// is no JSON for this table, this method will return nil. It is
|
||||
// important the caller closes the reader when done with it.
|
||||
OpenReader(tableName protoreflect.FullName) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// WriteTarget is a target for writing tables in JSON format. It
|
||||
// may abstract over a single JSON object or JSON in separate files that
|
||||
// can be written incrementally.
|
||||
type WriteTarget interface {
|
||||
// OpenWriter returns an io.WriteCloser for the named table. It is
|
||||
// important the caller closers the writer AND checks the error
|
||||
// when done with it.
|
||||
OpenWriter(tableName protoreflect.FullName) (io.WriteCloser, error)
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
package ormjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
type rawMessageSource struct {
|
||||
m map[string]json.RawMessage
|
||||
}
|
||||
|
||||
// NewRawMessageSource returns a new ReadSource for the provided
|
||||
// json.RawMessage where it is assumed that the raw message is a JSON
|
||||
// map where each table's JSON referenced by the map key corresponding
|
||||
// to the tables full protobuf name.
|
||||
func NewRawMessageSource(message json.RawMessage) (ReadSource, error) {
|
||||
var m map[string]json.RawMessage
|
||||
err := json.Unmarshal(message, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rawMessageSource{m}, err
|
||||
}
|
||||
|
||||
func (r rawMessageSource) OpenReader(tableName protoreflect.FullName) (io.ReadCloser, error) {
|
||||
j, ok := r.m[string(tableName)]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return readCloserWrapper{bytes.NewReader(j)}, nil
|
||||
}
|
||||
|
||||
type readCloserWrapper struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (r readCloserWrapper) Close() error { return nil }
|
||||
|
||||
var _ ReadSource = rawMessageSource{}
|
||||
|
||||
// RawMessageTarget is a WriteTarget wrapping a raw JSON map.
|
||||
type RawMessageTarget struct {
|
||||
m map[string]json.RawMessage
|
||||
}
|
||||
|
||||
// NewRawMessageTarget returns a new WriteTarget where each table's JSON
|
||||
// is written to a map key corresponding to the table's full protobuf name.
|
||||
func NewRawMessageTarget() *RawMessageTarget {
|
||||
return &RawMessageTarget{}
|
||||
}
|
||||
|
||||
func (r *RawMessageTarget) OpenWriter(tableName protoreflect.FullName) (io.WriteCloser, error) {
|
||||
if r.m == nil {
|
||||
r.m = map[string]json.RawMessage{}
|
||||
}
|
||||
|
||||
return &rawWriter{Buffer: &bytes.Buffer{}, sink: r, table: tableName}, nil
|
||||
}
|
||||
|
||||
// JSON returns the JSON map that was written as a json.RawMessage.
|
||||
func (r *RawMessageTarget) JSON() (json.RawMessage, error) {
|
||||
return json.MarshalIndent(r.m, "", " ")
|
||||
}
|
||||
|
||||
type rawWriter struct {
|
||||
*bytes.Buffer
|
||||
table protoreflect.FullName
|
||||
sink *RawMessageTarget
|
||||
}
|
||||
|
||||
func (r rawWriter) Close() error {
|
||||
r.sink.m[string(r.table)] = r.Buffer.Bytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ WriteTarget = &RawMessageTarget{}
|
||||
Loading…
Reference in New Issue
Block a user