encoding of query attrs
This commit is contained in:
parent
16d9291bc2
commit
7d43cd3ee4
@ -129,9 +129,9 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() {
|
|||||||
recAttr := helpers.UnMarshalMapFromJSONBytes(record.Attributes)
|
recAttr := helpers.UnMarshalMapFromJSONBytes(record.Attributes)
|
||||||
for _, attr := range test.req.GetAttributes() {
|
for _, attr := range test.req.GetAttributes() {
|
||||||
if attr.Key[:4] == "x500" {
|
if attr.Key[:4] == "x500" {
|
||||||
sr.Equal(keeper.GetAttributeValue(attr.Value), recAttr["x500"].(map[string]interface{})[attr.Key[4:]])
|
sr.Equal(keeper.EncodeAttributeValue(attr.Value), recAttr["x500"].(map[string]interface{})[attr.Key[4:]])
|
||||||
} else {
|
} else {
|
||||||
sr.Equal(keeper.GetAttributeValue(attr.Value), recAttr[attr.Key])
|
sr.Equal(keeper.EncodeAttributeValue(attr.Value), recAttr[attr.Key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,10 @@ import (
|
|||||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
cid "github.com/ipfs/go-cid"
|
||||||
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
"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"
|
basicnode "github.com/ipld/go-ipld-prime/node/basic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,10 +150,20 @@ func (k Keeper) ListRecords(ctx sdk.Context) []types.Record {
|
|||||||
|
|
||||||
// RecordsFromAttributes gets a list of records whose attributes match all provided values
|
// RecordsFromAttributes gets a list of records whose attributes match all provided values
|
||||||
func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.QueryListRecordsRequest_KeyValueInput, all bool) ([]types.Record, error) {
|
func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.QueryListRecordsRequest_KeyValueInput, all bool) ([]types.Record, error) {
|
||||||
|
log := logger(ctx).With("function", "RecordsFromAttributes")
|
||||||
|
|
||||||
resultRecordIds := []string{}
|
resultRecordIds := []string{}
|
||||||
for i, attr := range attributes {
|
for i, attr := range attributes {
|
||||||
val := GetAttributeValue(attr.Value)
|
val, err := EncodeAttributeValue2(attr.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
attributeIndex := GetAttributesIndexKey(attr.Key, val)
|
attributeIndex := GetAttributesIndexKey(attr.Key, val)
|
||||||
|
log.Debug("attribute index",
|
||||||
|
"key", attr.Key,
|
||||||
|
"value", val,
|
||||||
|
"value_type", fmt.Sprintf("%T", val),
|
||||||
|
"index", attributeIndex)
|
||||||
recordIds, err := k.GetAttributeMapping(ctx, attributeIndex)
|
recordIds, err := k.GetAttributeMapping(ctx, attributeIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -178,27 +191,38 @@ func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.Query
|
|||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAttributeValue(input *types.QueryListRecordsRequest_ValueInput) interface{} {
|
// TODO non recursive
|
||||||
|
func EncodeAttributeValue2(input *types.QueryListRecordsRequest_ValueInput) ([]byte, error) {
|
||||||
|
np := basicnode.Prototype.Any
|
||||||
|
nb := np.NewBuilder()
|
||||||
|
|
||||||
switch value := input.GetValue().(type) {
|
switch value := input.GetValue().(type) {
|
||||||
case *types.QueryListRecordsRequest_ValueInput_String_:
|
case *types.QueryListRecordsRequest_ValueInput_String_:
|
||||||
return value.String_
|
nb.AssignString(value.String_)
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Int:
|
case *types.QueryListRecordsRequest_ValueInput_Int:
|
||||||
return value.Int
|
nb.AssignInt(value.Int)
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Float:
|
case *types.QueryListRecordsRequest_ValueInput_Float:
|
||||||
return value.Float
|
nb.AssignFloat(value.Float)
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Boolean:
|
case *types.QueryListRecordsRequest_ValueInput_Boolean:
|
||||||
return value.Boolean
|
nb.AssignBool(value.Boolean)
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Link:
|
case *types.QueryListRecordsRequest_ValueInput_Link:
|
||||||
return value.Link
|
link := cidlink.Link{Cid: cid.MustParse(value.Link)}
|
||||||
|
nb.AssignLink(link)
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Array:
|
case *types.QueryListRecordsRequest_ValueInput_Array:
|
||||||
return value.Array
|
// TODO
|
||||||
case *types.QueryListRecordsRequest_ValueInput_Map:
|
case *types.QueryListRecordsRequest_ValueInput_Map:
|
||||||
return value.Map
|
// TODO
|
||||||
case nil:
|
|
||||||
return nil
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Value has unepxpected type %T", value)
|
return nil, fmt.Errorf("Value has unepxpected type %T", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n := nb.Build()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := dagjson.Encode(n, &buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
value := buf.Bytes()
|
||||||
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIntersection(a []string, b []string) []string {
|
func getIntersection(a []string, b []string) []string {
|
||||||
@ -251,7 +275,7 @@ func (k Keeper) GetRecordExpiryQueue(ctx sdk.Context) []*types.ExpiryQueueRecord
|
|||||||
// ProcessSetRecord creates a record.
|
// ProcessSetRecord creates a record.
|
||||||
func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*types.RecordEncodable, error) {
|
func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*types.RecordEncodable, error) {
|
||||||
payload := msg.Payload.ToReadablePayload()
|
payload := msg.Payload.ToReadablePayload()
|
||||||
record := types.RecordEncodable{Attributes: payload.Record, BondID: msg.BondId}
|
record := types.RecordEncodable{Attributes: payload.RecordAttributes, BondID: msg.BondId}
|
||||||
|
|
||||||
// Check signatures.
|
// Check signatures.
|
||||||
resourceSignBytes, _ := record.GetSignBytes()
|
resourceSignBytes, _ := record.GetSignBytes()
|
||||||
@ -342,19 +366,40 @@ func (k Keeper) PutRecord(ctx sdk.Context, record types.Record) {
|
|||||||
k.updateBlockChangeSetForRecord(ctx, record.Id)
|
k.updateBlockChangeSetForRecord(ctx, record.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) processAttributes(ctx sdk.Context, attrs map[string]any, id string, prefix string) error {
|
func (k Keeper) processAttributes(ctx sdk.Context, attrs []byte, id string, prefix string) error {
|
||||||
np := basicnode.Prototype.Any // Pick a stle for the in-memory data.
|
np := basicnode.Prototype.Map
|
||||||
nb := np.NewBuilder() // Create a builder.
|
nb := np.NewBuilder()
|
||||||
err := dagjson.Decode(nb, bytes.NewReader(content)) // Hand the builder to decoding -- decoding will fill it in!
|
err := dagjson.Decode(nb, bytes.NewReader(attrs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return 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)
|
||||||
}
|
}
|
||||||
n := nb.Build() // Call 'Build' to get the resulting Node. (It's immutable!)
|
|
||||||
|
|
||||||
for key, value := range attrs {
|
func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, prefix string) error {
|
||||||
if subRecord, ok := value.(map[string]any); ok {
|
for it := n.MapIterator(); !it.Done(); {
|
||||||
k.processAttributes(ctx, subRecord, id, key)
|
keynode, valuenode, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, err := keynode.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// for key, value := range attrs {
|
||||||
|
if valuenode.Kind() == ipld.Kind_Map {
|
||||||
|
k.processAttributeMap(ctx, valuenode, id, key)
|
||||||
} else {
|
} else {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := dagjson.Encode(valuenode, &buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value := buf.Bytes()
|
||||||
indexKey := GetAttributesIndexKey(prefix+key, value)
|
indexKey := GetAttributesIndexKey(prefix+key, value)
|
||||||
if err := k.SetAttributeMapping(ctx, indexKey, id); err != nil {
|
if err := k.SetAttributeMapping(ctx, indexKey, id); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -17,9 +17,15 @@ const (
|
|||||||
// become specific to content records. schema records will either occupy a new message or have new
|
// become specific to content records. schema records will either occupy a new message or have new
|
||||||
// more general purpose helper types.
|
// more general purpose helper types.
|
||||||
|
|
||||||
|
type DagJsonBlob []byte
|
||||||
|
|
||||||
|
func (b DagJsonBlob) MarshalJSON() ([]byte, error) {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PayloadEncodable represents a signed record payload that can be serialized from/to YAML.
|
// PayloadEncodable represents a signed record payload that can be serialized from/to YAML.
|
||||||
type PayloadEncodable struct {
|
type PayloadEncodable struct {
|
||||||
Record map[string]interface{} `json:"record"`
|
RecordAttributes DagJsonBlob `json:"record"`
|
||||||
Signatures []Signature `json:"signatures"`
|
Signatures []Signature `json:"signatures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +33,7 @@ type PayloadEncodable struct {
|
|||||||
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
||||||
func (payloadObj *PayloadEncodable) ToPayload() Payload {
|
func (payloadObj *PayloadEncodable) ToPayload() Payload {
|
||||||
// Note: record directly contains the attributes here
|
// Note: record directly contains the attributes here
|
||||||
attributes := helpers.MarshalMapToJSONBytes(payloadObj.Record)
|
attributes := payloadObj.RecordAttributes
|
||||||
payload := Payload{
|
payload := Payload{
|
||||||
Record: &Record{
|
Record: &Record{
|
||||||
Deleted: false,
|
Deleted: false,
|
||||||
@ -36,7 +42,6 @@ func (payloadObj *PayloadEncodable) ToPayload() Payload {
|
|||||||
},
|
},
|
||||||
Signatures: payloadObj.Signatures,
|
Signatures: payloadObj.Signatures,
|
||||||
}
|
}
|
||||||
// TODO rm error
|
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +49,7 @@ func (payloadObj *PayloadEncodable) ToPayload() Payload {
|
|||||||
func (payload Payload) ToReadablePayload() PayloadEncodable {
|
func (payload Payload) ToReadablePayload() PayloadEncodable {
|
||||||
var encodable PayloadEncodable
|
var encodable PayloadEncodable
|
||||||
|
|
||||||
encodable.Record = helpers.UnMarshalMapFromJSONBytes(payload.Record.Attributes)
|
encodable.RecordAttributes = payload.Record.Attributes
|
||||||
encodable.Signatures = payload.Signatures
|
encodable.Signatures = payload.Signatures
|
||||||
|
|
||||||
return encodable
|
return encodable
|
||||||
@ -61,7 +66,7 @@ func (r *Record) ToReadableRecord() RecordEncodable {
|
|||||||
resourceObj.Deleted = r.Deleted
|
resourceObj.Deleted = r.Deleted
|
||||||
resourceObj.Owners = r.Owners
|
resourceObj.Owners = r.Owners
|
||||||
resourceObj.Names = r.Names
|
resourceObj.Names = r.Names
|
||||||
resourceObj.Attributes = helpers.UnMarshalMapFromJSONBytes(r.Attributes)
|
resourceObj.Attributes = r.Attributes
|
||||||
|
|
||||||
return resourceObj
|
return resourceObj
|
||||||
}
|
}
|
||||||
@ -75,14 +80,13 @@ type RecordEncodable struct {
|
|||||||
ExpiryTime string `json:"expiryTime,omitempty"`
|
ExpiryTime string `json:"expiryTime,omitempty"`
|
||||||
Deleted bool `json:"deleted,omitempty"`
|
Deleted bool `json:"deleted,omitempty"`
|
||||||
Owners []string `json:"owners,omitempty"`
|
Owners []string `json:"owners,omitempty"`
|
||||||
Attributes map[string]interface{} `json:"attributes,omitempty"`
|
// Attributes map[string]interface{} `json:"attributes,omitempty"`
|
||||||
|
Attributes DagJsonBlob `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToRecordObj converts Record to RecordObj.
|
// ToRecordObj converts Record to RecordObj.
|
||||||
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
||||||
func (r *RecordEncodable) ToRecordObj() (Record, error) {
|
func (r *RecordEncodable) ToRecordObj() (Record, error) {
|
||||||
attributes := helpers.MarshalMapToJSONBytes(r.Attributes)
|
|
||||||
|
|
||||||
var resourceObj Record
|
var resourceObj Record
|
||||||
|
|
||||||
resourceObj.Id = r.ID
|
resourceObj.Id = r.ID
|
||||||
@ -91,7 +95,7 @@ func (r *RecordEncodable) ToRecordObj() (Record, error) {
|
|||||||
resourceObj.ExpiryTime = r.ExpiryTime
|
resourceObj.ExpiryTime = r.ExpiryTime
|
||||||
resourceObj.Deleted = r.Deleted
|
resourceObj.Deleted = r.Deleted
|
||||||
resourceObj.Owners = r.Owners
|
resourceObj.Owners = r.Owners
|
||||||
resourceObj.Attributes = attributes
|
resourceObj.Attributes = r.Attributes
|
||||||
|
|
||||||
return resourceObj, nil
|
return resourceObj, nil
|
||||||
}
|
}
|
||||||
@ -100,7 +104,7 @@ func (r *RecordEncodable) ToRecordObj() (Record, error) {
|
|||||||
func (r *RecordEncodable) CanonicalJSON() []byte {
|
func (r *RecordEncodable) CanonicalJSON() []byte {
|
||||||
bytes, err := canonicalJson.Marshal(r.Attributes)
|
bytes, err := canonicalJson.Marshal(r.Attributes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Record marshal error.")
|
panic("Record marshal error: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes
|
return bytes
|
||||||
|
Loading…
Reference in New Issue
Block a user