Graphql schema for DAG-JSON objects #1

Merged
ashwin merged 13 commits from ng-gql-ipld into ng-rm-record-types 2024-01-11 09:46:41 +00:00
4 changed files with 72 additions and 70 deletions
Showing only changes of commit df6d8d8234 - Show all commits

View File

@ -47,24 +47,20 @@ func Int64ToBytes(num int64) []byte {
return buf.Bytes()
}
// MarshalMapToJSONBytes converts map[string]interface{} to bytes.
func MarshalMapToJSONBytes(val map[string]interface{}) (bytes []byte) {
func MustMarshalJSON[T any](val T) (bytes []byte) {
bytes, err := json.Marshal(val)
if err != nil {
panic("Marshal error.")
panic("JSON marshal error:" + err.Error())
}
return
}
// UnMarshalMapFromJSONBytes converts bytes to map[string]interface{}.
func UnMarshalMapFromJSONBytes(bytes []byte) map[string]interface{} {
var val map[string]interface{}
func MustUnmarshalJSON[T any](bytes []byte) T {
var val T
err := json.Unmarshal(bytes, &val)
if err != nil {
panic("Unmarshal error.")
panic("JSON unmarshal error:" + err.Error())
}
return val
}

View File

@ -182,9 +182,13 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
sr.Equal(resp.GetRecords()[0].GetBondId(), suite.bond.GetId())
for _, record := range resp.GetRecords() {
recAttr := helpers.UnMarshalMapFromJSONBytes(record.Attributes)
recAttr := helpers.MustUnmarshalJSON[registrytypes.AttributeMap](record.Attributes)
for _, attr := range test.req.GetAttributes() {
av := keeper.GetAttributeValue(attr.Value)
enc, err := keeper.QueryValueToJSON(attr.Value)
sr.NoError(err)
av := helpers.MustUnmarshalJSON[any](enc)
if nil != av && nil != recAttr[attr.Key] &&
reflect.Slice == reflect.TypeOf(recAttr[attr.Key]).Kind() &&
reflect.Slice != reflect.TypeOf(av).Kind() {

View File

@ -8,10 +8,6 @@ import (
"time"
errorsmod "cosmossdk.io/errors"
auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper"
bondkeeper "github.com/cerc-io/laconicd/x/bond/keeper"
"github.com/cerc-io/laconicd/x/registry/helpers"
"github.com/cerc-io/laconicd/x/registry/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
@ -20,13 +16,18 @@ import (
auth "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bank "github.com/cosmos/cosmos-sdk/x/bank/keeper"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/tendermint/tendermint/libs/log"
"github.com/gibson042/canonicaljson-go"
cid "github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/codec/dagjson"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
basicnode "github.com/ipld/go-ipld-prime/node/basic"
"github.com/tendermint/tendermint/libs/log"
auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper"
bondkeeper "github.com/cerc-io/laconicd/x/bond/keeper"
"github.com/cerc-io/laconicd/x/registry/helpers"
"github.com/cerc-io/laconicd/x/registry/types"
)
var (
@ -152,7 +153,7 @@ func (k Keeper) ListRecords(ctx sdk.Context) []types.Record {
func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.QueryListRecordsRequest_KeyValueInput, all bool) ([]types.Record, error) {
resultRecordIds := []string{}
for i, attr := range attributes {
suffix, err := EncodeQueryValue(attr.Value)
suffix, err := QueryValueToJSON(attr.Value)
if err != nil {
return nil, err
}
@ -184,8 +185,9 @@ func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.Query
return records, nil
}
// TODO not recursive, and only should be if we want to support querying with whole sub-objects
func EncodeQueryValue(input *types.QueryListRecordsRequest_ValueInput) ([]byte, error) {
// TODO not recursive, and only should be if we want to support querying with whole sub-objects,
// which seems unnecessary.
func QueryValueToJSON(input *types.QueryListRecordsRequest_ValueInput) ([]byte, error) {
np := basicnode.Prototype.Any
nb := np.NewBuilder()
@ -202,20 +204,19 @@ func EncodeQueryValue(input *types.QueryListRecordsRequest_ValueInput) ([]byte,
link := cidlink.Link{Cid: cid.MustParse(value.Link)}
nb.AssignLink(link)
case *types.QueryListRecordsRequest_ValueInput_Array:
// TODO
return nil, fmt.Errorf("Recursive query values are not supported")
case *types.QueryListRecordsRequest_ValueInput_Map:
// TODO
return nil, fmt.Errorf("Recursive query values are not supported")
default:
return nil, fmt.Errorf("Value has unepxpected type %T", value)
return nil, fmt.Errorf("Value has unexpected type %T", value)
}
n := nb.Build()
var buf bytes.Buffer
if err := dagjson.Encode(n, &buf); err != nil {
return nil, err
return nil, fmt.Errorf("encoding value to JSON failed: %w", err)
}
value := buf.Bytes()
return value, nil
return buf.Bytes(), nil
}
func getIntersection(a []string, b []string) []string {
@ -288,14 +289,12 @@ func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*type
for _, sig := range payload.Signatures {
pubKey, err := legacy.PubKeyFromBytes(helpers.BytesFromBase64(sig.PubKey))
if err != nil {
fmt.Println("Error decoding pubKey from bytes: ", err)
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid public key.")
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Error decoding pubKey from bytes: ", err))
}
sigOK := pubKey.VerifySignature(resourceSignBytes, helpers.BytesFromBase64(sig.Sig))
if !sigOK {
fmt.Println("Signature mismatch: ", sig.PubKey)
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid signature.")
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Signature mismatch: ", sig.PubKey))
}
record.Owners = append(record.Owners, pubKey.Address().String())
}
@ -358,17 +357,25 @@ func (k Keeper) PutRecord(ctx sdk.Context, record types.Record) {
k.updateBlockChangeSetForRecord(ctx, record.Id)
}
func (k Keeper) processAttributes(ctx sdk.Context, attrs []byte, id string, prefix string) error {
func (k Keeper) processAttributes(ctx sdk.Context, attrs types.AttributeMap, id string, prefix string) error {
np := basicnode.Prototype.Map
nb := np.NewBuilder()
err := dagjson.Decode(nb, bytes.NewReader(attrs))
encAttrs, err := canonicaljson.Marshal(attrs)
if err != nil {
return err
}
if len(attrs) == 0 {
encAttrs = []byte("{}")
}
err = dagjson.Decode(nb, bytes.NewReader(encAttrs))
if err != nil {
return fmt.Errorf("failed to decode attributes: %w", err)
}
n := nb.Build()
if n.Kind() != ipld.Kind_Map {
return fmt.Errorf("Record attributes must be a map, not %T", n.Kind())
}
return k.processAttributeMap(ctx, n, id, prefix)
}
@ -406,7 +413,6 @@ func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, pre
func GetAttributesIndexKey(key string, suffix []byte) []byte {
keyString := fmt.Sprintf("%s=%s", key, suffix)
return append(PrefixAttributesIndex, []byte(keyString)...)
// return append(append(PrefixAttributesIndex, key...), suffix...)
}
func (k Keeper) SetAttributeMapping(ctx sdk.Context, key []byte, recordID string) error {

View File

@ -4,7 +4,7 @@ import (
"crypto/sha256"
"github.com/cerc-io/laconicd/x/registry/helpers"
canonicalJson "github.com/gibson042/canonicaljson-go"
"github.com/gibson042/canonicaljson-go"
)
const (
@ -17,28 +17,24 @@ const (
// become specific to content records. schema records will either occupy a new message or have new
// more general purpose helper types.
type DagJsonBlob []byte
func (b DagJsonBlob) MarshalJSON() ([]byte, error) {
return b, nil
}
type AttributeMap map[string]interface{}
// ReadablePayload represents a signed record payload that can be serialized from/to YAML.
type ReadablePayload struct {
Record map[string]interface{} `json:"record"`
Signatures []Signature `json:"signatures"`
RecordAttributes AttributeMap `json:"record" yaml:"record"`
Signatures []Signature `json:"signatures" yaml:"signatures"`
}
// ReadableRecord represents a WNS record.
type ReadableRecord struct {
ID string `json:"id,omitempty"`
Names []string `json:"names,omitempty"`
BondID string `json:"bondId,omitempty"`
CreateTime string `json:"createTime,omitempty"`
ExpiryTime string `json:"expiryTime,omitempty"`
Deleted bool `json:"deleted,omitempty"`
Owners []string `json:"owners,omitempty"`
Attributes map[string]interface{} `json:"attributes,omitempty"`
ID string `json:"id,omitempty"`
Names []string `json:"names,omitempty"`
BondID string `json:"bondId,omitempty"`
CreateTime string `json:"createTime,omitempty"`
ExpiryTime string `json:"expiryTime,omitempty"`
Deleted bool `json:"deleted,omitempty"`
Owners []string `json:"owners,omitempty"`
Attributes AttributeMap `json:"attributes,omitempty"`
}
// ToPayload converts PayloadEncodable to Payload object.
@ -50,7 +46,7 @@ func (payloadObj *ReadablePayload) ToPayload() Payload {
Record: &Record{
Deleted: false,
Owners: nil,
Attributes: attributes,
Attributes: helpers.MustMarshalJSON(attributes),
},
Signatures: payloadObj.Signatures,
}
@ -61,28 +57,12 @@ func (payloadObj *ReadablePayload) ToPayload() Payload {
func (payload Payload) ToReadablePayload() ReadablePayload {
var encodable ReadablePayload
encodable.Record = helpers.UnMarshalMapFromJSONBytes(payload.Record.Attributes)
encodable.RecordAttributes = helpers.MustUnmarshalJSON[AttributeMap](payload.Record.Attributes)
encodable.Signatures = payload.Signatures
return encodable
}
// ToReadableRecord converts Record to a serializable object
func (r *Record) ToReadableRecord() ReadableRecord {
var resourceObj ReadableRecord
resourceObj.ID = r.Id
resourceObj.BondID = r.BondId
resourceObj.CreateTime = r.CreateTime
resourceObj.ExpiryTime = r.ExpiryTime
resourceObj.Deleted = r.Deleted
resourceObj.Owners = r.Owners
resourceObj.Names = r.Names
resourceObj.Attributes = helpers.UnMarshalMapFromJSONBytes(r.Attributes)
return resourceObj
}
// ToRecordObj converts Record to RecordObj.
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
func (r *ReadableRecord) ToRecordObj() (Record, error) {
@ -96,16 +76,32 @@ func (r *ReadableRecord) ToRecordObj() (Record, error) {
resourceObj.ExpiryTime = r.ExpiryTime
resourceObj.Deleted = r.Deleted
resourceObj.Owners = r.Owners
resourceObj.Attributes = attributes
resourceObj.Attributes = helpers.MustMarshalJSON(r.Attributes)
return resourceObj, nil
}
// ToReadableRecord converts Record to a serializable object
func (r *Record) ToReadableRecord() RecordEncodable {
var resourceObj RecordEncodable
resourceObj.ID = r.Id
resourceObj.BondID = r.BondId
resourceObj.CreateTime = r.CreateTime
resourceObj.ExpiryTime = r.ExpiryTime
resourceObj.Deleted = r.Deleted
resourceObj.Owners = r.Owners
resourceObj.Names = r.Names
resourceObj.Attributes = helpers.MustUnmarshalJSON[AttributeMap](r.Attributes)
return resourceObj
}
// CanonicalJSON returns the canonical JSON representation of the record.
func (r *ReadableRecord) CanonicalJSON() []byte {
bytes, err := canonicalJson.Marshal(r.Attributes)
bytes, err := canonicaljson.Marshal(r.Attributes)
if err != nil {
panic("Record marshal error: " + err.Error())
panic("error marshalling record: " + err.Error())
}
return bytes