Skip to content

Commit

Permalink
Merge pull request #17 from GoLedgerDev/develop
Browse files Browse the repository at this point in the history
Dynamic Asset Type funcionality
  • Loading branch information
samuelvenzi authored Feb 7, 2023
2 parents 128f971 + f9c62d4 commit 91eefe1
Show file tree
Hide file tree
Showing 23 changed files with 2,941 additions and 0 deletions.
5 changes: 5 additions & 0 deletions assets/assetList.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
package assets

import "time"

// assetTypeList is the list which should contain all defined asset types
var assetTypeList = []AssetType{}

// listUpdateTime is the last time the assetTypeList was updated
var listUpdateTime time.Time
197 changes: 197 additions & 0 deletions assets/assetListFuncs.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package assets

import (
"net/http"
"time"

"github.com/goledgerdev/cc-tools/errors"
sw "github.com/goledgerdev/cc-tools/stubwrapper"
)

// AssetTypeList returns a copy of the assetTypeList variable.
func AssetTypeList() []AssetType {
listCopy := make([]AssetType, len(assetTypeList))
Expand All @@ -19,5 +27,194 @@ func FetchAssetType(assetTypeTag string) *AssetType {

// InitAssetList appends custom assets to assetTypeList to avoid initialization loop.
func InitAssetList(l []AssetType) {
if GetEnabledDynamicAssetType() {
l = append(l, GetListAssetType())
}
assetTypeList = l
}

// ReplaceAssetList replace assetTypeList to for a new one
func ReplaceAssetList(l []AssetType) {
assetTypeList = l
}

// UpdateAssetList updates the assetTypeList variable on runtime
func UpdateAssetList(l []AssetType) {
assetTypeList = append(assetTypeList, l...)
}

// RemoveAssetType removes an asset type from an AssetType List and returns the new list
func RemoveAssetType(tag string, l []AssetType) []AssetType {
for i, assetType := range l {
if assetType.Tag == tag {
l = append(l[:i], l[i+1:]...)
}
}
return l
}

// ReplaceAssetType replaces an asset type from an AssetType List with an updated version and returns the new list
// This function does not automatically update the assetTypeList variable
func ReplaceAssetType(assetType AssetType, l []AssetType) []AssetType {
for i, v := range l {
if v.Tag == assetType.Tag {
l[i] = assetType
}
}
return l
}

// StoreAssetList stores the current assetList on the blockchain
func StoreAssetList(stub *sw.StubWrapper) errors.ICCError {
assetList := AssetTypeList()
l := ArrayFromAssetTypeList(assetList)

txTimestamp, err := stub.Stub.GetTxTimestamp()
if err != nil {
return errors.WrapError(err, "failed to get tx timestamp")
}
txTime := txTimestamp.AsTime()
txTimeStr := txTime.Format(time.RFC3339)

listKey, err := NewKey(map[string]interface{}{
"@assetType": "assetTypeListData",
"id": "primary",
})
if err != nil {
return errors.NewCCError("error getting asset list key", http.StatusInternalServerError)
}

exists, err := listKey.ExistsInLedger(stub)
if err != nil {
return errors.NewCCError("error checking if asset list exists", http.StatusInternalServerError)
}

if exists {
listAsset, err := listKey.Get(stub)
if err != nil {
return errors.WrapError(err, "error getting asset list")
}
listMap := (map[string]interface{})(*listAsset)

listMap["list"] = l
listMap["lastUpdated"] = txTimeStr

_, err = listAsset.Update(stub, listMap)
if err != nil {
return errors.WrapError(err, "error updating asset list")
}
} else {
listMap := map[string]interface{}{
"@assetType": "assetTypeListData",
"id": "primary",
"list": l,
"lastUpdated": txTimeStr,
}

listAsset, err := NewAsset(listMap)
if err != nil {
return errors.WrapError(err, "error creating asset list")
}

_, err = listAsset.PutNew(stub)
if err != nil {
return errors.WrapError(err, "error putting asset list")
}
}

SetAssetListUpdateTime(txTime)

return nil
}

// RestoreAssetList restores the assetList from the blockchain
func RestoreAssetList(stub *sw.StubWrapper, init bool) errors.ICCError {
listKey, err := NewKey(map[string]interface{}{
"@assetType": "assetTypeListData",
"id": "primary",
})
if err != nil {
return errors.NewCCError("error gettin asset list key", http.StatusInternalServerError)
}

exists, err := listKey.ExistsInLedger(stub)
if err != nil {
return errors.NewCCError("error checking if asset list exists", http.StatusInternalServerError)
}

if exists {
listAsset, err := listKey.Get(stub)
if err != nil {
return errors.NewCCError("error getting asset list", http.StatusInternalServerError)
}
listMap := (map[string]interface{})(*listAsset)

txTime := listMap["lastUpdated"].(time.Time)

if GetAssetListUpdateTime().After(txTime) {
return nil
}

l := AssetTypeListFromArray(listMap["list"].([]interface{}))

l = getRestoredList(l, init)

ReplaceAssetList(l)

SetAssetListUpdateTime(txTime)
}

return nil
}

// getRestoredList reconstructs the assetTypeList from the stored list comparing to the current list
func getRestoredList(storedList []AssetType, init bool) []AssetType {
assetList := AssetTypeList()

deleteds := AssetTypeList()

for _, assetTypeStored := range storedList {
if !assetTypeStored.Dynamic {
continue
}

exists := false
for i, assetType := range assetList {
if assetType.Tag == assetTypeStored.Tag {
exists = true

assetTypeStored.Validate = assetType.Validate
assetList[i] = assetTypeStored

deleteds = RemoveAssetType(assetType.Tag, deleteds)

break
}
}
if !exists {
assetList = append(assetList, assetTypeStored)
}
}

if !init {
for _, deleted := range deleteds {
if !deleted.Dynamic {
continue
}

assetList = RemoveAssetType(deleted.Tag, assetList)
}
}

return assetList
}

// GetAssetListUpdateTime returns the last time the asset list was updated
func GetAssetListUpdateTime() time.Time {
return listUpdateTime
}

// SetAssetListUpdateTime sets the last time the asset list was updated
func SetAssetListUpdateTime(t time.Time) {
listUpdateTime = t
}
81 changes: 81 additions & 0 deletions assets/assetProp.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,84 @@ type AssetProp struct {
// Validate is a function called when validating property format.
Validate func(interface{}) error `json:"-"`
}

// ToMap converts an AssetProp to a map[string]interface{}
func (p AssetProp) ToMap() map[string]interface{} {
return map[string]interface{}{
"tag": p.Tag,
"label": p.Label,
"description": p.Description,
"isKey": p.IsKey,
"required": p.Required,
"readOnly": p.ReadOnly,
"defaultValue": p.DefaultValue,
"dataType": p.DataType,
"writers": p.Writers,
}
}

// AssetPropFromMap converts a map[string]interface{} to an AssetProp
func AssetPropFromMap(m map[string]interface{}) AssetProp {
description, ok := m["description"].(string)
if !ok {
description = ""
}
label, ok := m["label"].(string)
if !ok {
label = ""
}
isKey, ok := m["isKey"].(bool)
if !ok {
isKey = false
}
required, ok := m["required"].(bool)
if !ok {
required = false
}
readOnly, ok := m["readOnly"].(bool)
if !ok {
readOnly = false
}

res := AssetProp{
Tag: m["tag"].(string),
Label: label,
Description: description,
IsKey: isKey,
Required: required,
ReadOnly: readOnly,
DefaultValue: m["defaultValue"],
DataType: m["dataType"].(string),
}

writers := make([]string, 0)
writersArr, ok := m["writers"].([]interface{})
if ok {
for _, w := range writersArr {
writers = append(writers, w.(string))
}
}
if len(writers) > 0 {
res.Writers = writers
}

return res
}

// ArrayFromAssetPropList converts an array of AssetProp to an array of map[string]interface
func ArrayFromAssetPropList(a []AssetProp) []map[string]interface{} {
list := []map[string]interface{}{}
for _, m := range a {
list = append(list, m.ToMap())
}
return list
}

// AssetPropListFromArray converts an array of map[string]interface to an array of AssetProp
func AssetPropListFromArray(a []interface{}) []AssetProp {
list := []AssetProp{}
for _, m := range a {
list = append(list, AssetPropFromMap(m.(map[string]interface{})))
}
return list
}
68 changes: 68 additions & 0 deletions assets/assetType.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type AssetType struct {

// Validate is a function called when validating asset as a whole.
Validate func(Asset) error `json:"-"`

// Dynamic is a flag that indicates if the asset type is dynamic.
Dynamic bool `json:"dynamic,omitempty"`
}

// Keys returns a list of asset properties which are defined as primary keys. (IsKey == true)
Expand Down Expand Up @@ -77,3 +80,68 @@ func (t AssetType) GetPropDef(propTag string) *AssetProp {
func (t AssetType) IsPrivate() bool {
return len(t.Readers) > 0
}

// ToMap returns a map representation of the asset type.
func (t AssetType) ToMap() map[string]interface{} {
return map[string]interface{}{
"tag": t.Tag,
"label": t.Label,
"description": t.Description,
"props": ArrayFromAssetPropList(t.Props),
"readers": t.Readers,
"dynamic": t.Dynamic,
}
}

// AssetTypeFromMap returns an asset type from a map representation.
func AssetTypeFromMap(m map[string]interface{}) AssetType {
label, ok := m["label"].(string)
if !ok {
label = ""
}
description, ok := m["description"].(string)
if !ok {
description = ""
}
dynamic, ok := m["dynamic"].(bool)
if !ok {
dynamic = false
}

res := AssetType{
Tag: m["tag"].(string),
Label: label,
Description: description,
Props: AssetPropListFromArray(m["props"].([]interface{})),
Dynamic: dynamic,
}

readers := make([]string, 0)
readersArr, ok := m["readers"].([]interface{})
if ok {
for _, r := range readersArr {
readers = append(readers, r.(string))
}
}
if len(readers) > 0 {
res.Readers = readers
}

return res
}

// ArrayFromAssetTypeList converts an array of AssetType to an array of map[string]interface
func ArrayFromAssetTypeList(assetTypes []AssetType) (array []map[string]interface{}) {
for _, assetType := range assetTypes {
array = append(array, assetType.ToMap())
}
return
}

// AssetTypeListFromArray converts an array of map[string]interface to an array of AssetType
func AssetTypeListFromArray(array []interface{}) (assetTypes []AssetType) {
for _, v := range array {
assetTypes = append(assetTypes, AssetTypeFromMap(v.(map[string]interface{})))
}
return
}
15 changes: 15 additions & 0 deletions assets/dynamicAssetType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package assets

// DynamicAssetType is the configuration for the Dynamic AssetTypes
type DynamicAssetType struct {
// Enabled defines whether the Dynamic AssetTypes feature is active
Enabled bool `json:"enabled"`

// AssetAdmins is an array that specifies which organizations can operate the Dynamic AssetTyper feature.
// Accepts either basic strings for exact matches
// eg. []string{'org1MSP', 'org2MSP'}
// or regular expressions
// eg. []string{`$org\dMSP`} and cc-tools will
// check for a match with regular expression `org\dMSP`
AssetAdmins []string `json:"assetAdmins"`
}
Loading

0 comments on commit 91eefe1

Please sign in to comment.