feat(orm): ModuleDB JSON import/export/default/validate methods (#11101)

* feat(orm): ModuleDB JSON methods

* WIP

* WIP on JSON

* WIP

* WIP

* tests and docs

* revert

* tests and docs

* docs

* address review comments
This commit is contained in:
Aaron Craelius
2022-02-03 20:36:21 -08:00
committed by GitHub
parent 888548ab53
commit 37ae08d92a
26 changed files with 393 additions and 45 deletions
+119
View File
@@ -0,0 +1,119 @@
package ormdb
import (
"context"
"fmt"
"sort"
"github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/cosmos/cosmos-sdk/errors"
)
func (m moduleDB) DefaultJSON(target ormjson.WriteTarget) error {
for name, table := range m.tablesByName {
w, err := target.OpenWriter(name)
if err != nil {
return err
}
_, err = w.Write(table.DefaultJSON())
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
}
return nil
}
func (m moduleDB) ValidateJSON(source ormjson.ReadSource) error {
errMap := map[protoreflect.FullName]error{}
for name, table := range m.tablesByName {
r, err := source.OpenReader(name)
if err != nil {
return err
}
err = table.ValidateJSON(r)
if err != nil {
errMap[name] = err
}
err = r.Close()
if err != nil {
return err
}
}
if len(errMap) != 0 {
var allErrors string
for name, err := range errMap {
allErrors += fmt.Sprintf("Error in JSON for table %s: %v\n", name, err)
}
return ormerrors.JSONValidationError.Wrap(allErrors)
}
return nil
}
func (m moduleDB) ImportJSON(ctx context.Context, source ormjson.ReadSource) error {
var names []string
for name := range m.tablesByName {
names = append(names, string(name))
}
sort.Strings(names)
for _, name := range names {
fullName := protoreflect.FullName(name)
table := m.tablesByName[fullName]
r, err := source.OpenReader(fullName)
if err != nil {
return errors.Wrapf(err, "table %s", fullName)
}
if r == nil {
continue
}
err = table.ImportJSON(ctx, r)
if err != nil {
return errors.Wrapf(err, "table %s", fullName)
}
err = r.Close()
if err != nil {
return errors.Wrapf(err, "table %s", fullName)
}
}
return nil
}
func (m moduleDB) ExportJSON(ctx context.Context, sink ormjson.WriteTarget) error {
for name, table := range m.tablesByName {
w, err := sink.OpenWriter(name)
if err != nil {
return err
}
err = table.ExportJSON(ctx, w)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
}
return nil
}
+18 -1
View File
@@ -6,6 +6,8 @@ import (
"encoding/binary"
"math"
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
"google.golang.org/protobuf/reflect/protodesc"
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
@@ -31,7 +33,22 @@ type ModuleSchema struct {
}
// ModuleDB defines the ORM database type to be used by modules.
type ModuleDB = ormtable.Schema
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
+41 -5
View File
@@ -3,10 +3,17 @@ package ormdb_test
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
"github.com/cosmos/cosmos-sdk/orm/testing/ormtest"
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
"google.golang.org/protobuf/reflect/protoreflect"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
@@ -157,14 +164,14 @@ func TestModuleDB(t *testing.T) {
db, err := ormdb.NewModuleDB(TestBankSchema, ormdb.ModuleDBOptions{})
assert.NilError(t, err)
debugBuf := &strings.Builder{}
store := testkv.NewDebugBackend(
testkv.NewSharedMemBackend(),
backend := ormtest.NewMemoryBackend()
ctx := ormtable.WrapContextDefault(testkv.NewDebugBackend(
backend,
&testkv.EntryCodecDebugger{
EntryCodec: db,
Print: func(s string) { debugBuf.WriteString(s + "\n") },
},
)
ctx := ormtable.WrapContextDefault(store)
))
// create keeper
k, err := newKeeper(db)
@@ -205,7 +212,7 @@ func TestModuleDB(t *testing.T) {
golden.Assert(t, debugBuf.String(), "bank_scenario.golden")
// check decode & encode
it, err := store.CommitmentStore().Iterator(nil, nil)
it, err := backend.CommitmentStore().Iterator(nil, nil)
assert.NilError(t, err)
for it.Valid() {
entry, err := db.DecodeEntry(it.Key(), it.Value())
@@ -216,4 +223,33 @@ func TestModuleDB(t *testing.T) {
assert.Assert(t, bytes.Equal(v, it.Value()))
it.Next()
}
// check JSON
target := ormjson.NewRawMessageTarget()
assert.NilError(t, db.DefaultJSON(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))
rawJson, err = target.JSON()
assert.NilError(t, err)
badJSON := `{
"testpb.Balance": 5,
"testpb.Supply": {}
}
`
source, err := ormjson.NewRawMessageSource(json.RawMessage(badJSON))
assert.NilError(t, err)
assert.ErrorIs(t, db.ValidateJSON(source), ormerrors.JSONValidationError)
backend2 := ormtest.NewMemoryBackend()
ctx2 := ormtable.WrapContextDefault(backend2)
source, err = ormjson.NewRawMessageSource(rawJson)
assert.NilError(t, err)
assert.NilError(t, db.ValidateJSON(source))
assert.NilError(t, db.ImportJSON(ctx2, source))
testkv.AssertBackendsEqual(t, backend, backend2)
}
+4
View File
@@ -0,0 +1,4 @@
{
"testpb.Balance": [],
"testpb.Supply": []
}