/** * controller.go * * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. */ package discard import ( "errors" "git.code.tecnalia.com/ledgerbuilder/sdk/core/api" "git.code.tecnalia.com/ledgerbuilder/sdk/core/controller" "git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos" "git.code.tecnalia.com/ledgerbuilder/sdk/shared" "git.code.tecnalia.com/blockchain/hypercog/controller/stats" errs "git.code.tecnalia.com/traceblock/sdk/constants" "git.code.tecnalia.com/traceblock/sdk/controller/base" "git.code.tecnalia.com/traceblock/sdk/controller/split" "git.code.tecnalia.com/traceblock/sdk/model" ) type DiscardController struct { base.TraceblockBaseController } var ( errInvalidInputData = errors.New("failed to read model") errInvalidDiscardQuantity = errors.New("quantity to discard is greater than the available quantity") ) // constructor like function func NewDiscardController() *DiscardController { ctl := new(DiscardController) ctl.LowLevelController = controller.NewLowLevelController() ctl.SetDataModelClosure(ctl.modelClosure) return ctl } func (c DiscardController) modelClosure(stub shared.LedgerBuildrStubInterface) shared.LedgerBuildrAsset { return NewDiscardParams() } func (c DiscardController) readTraceableAsset(stub shared.LedgerBuildrStubInterface, assetId string) (*model.TraceableAsset, error) { readedModel, err := c.ReadAbstractAssetAndReturn(stub, assetId, model.NewTraceableAsset()) if err != nil { return nil, err } // asset data successfully readed asset, ok := readedModel.(*model.TraceableAsset) if !ok { return nil, errs.NotExistingAsset } return asset, nil } func (c DiscardController) readAsset(stub shared.LedgerBuildrStubInterface, requestAsset shared.LedgerBuildrAsset) (*model.TraceableAsset, error) { return c.readTraceableAsset(stub, requestAsset.GetID()) } // HyperCOG specific discard operation func (c DiscardController) _discardAsset(stub shared.LedgerBuildrStubInterface, params *DiscardParams, discardableAsset *model.TraceableAsset) (*protos.Response, error) { if discardableAsset.Quantity < params.Quantity { return nil, errInvalidDiscardQuantity } var discardedStock *model.TraceableAsset generatedIds := make([]string, 2) if params.Quantity == discardableAsset.Quantity { discardedStock = discardableAsset } else { // discardableAsset.Quantity > discardParams.Quantity splitParams := new(split.SplitParams) splitParams.SetID(discardableAsset.GetID()) splitParams.SplitConfig.ArchiveOld = true splitParams.SplitConfig.Bidirectional = true splitParams.SplitConfig.ChildCount = 2 splitParams.SplitConfig.BaseAsset.AssetType = discardableAsset.AssetType splitParams.SplitConfig.BaseAsset.ArbitraryDataFields = discardableAsset.ArbitraryDataFields subAssets, err := split.SplitAsset(c.TraceblockBaseController, stub, discardableAsset, *splitParams) if err != nil { return nil, err } for i, c := range subAssets { generatedIds[i] = c.GetID() } newStock := subAssets[0] newStock.Quantity = discardableAsset.Quantity - params.Quantity newStock.Units = discardableAsset.Units respNewStock:= c.SaveAbstractAsset(stub, &newStock) if respNewStock.Status != shared.OK { return &respNewStock, nil } discardedStock = &subAssets[1] discardedStock.Quantity = params.Quantity discardedStock.Units = discardableAsset.Units discardedStock.Add("name", " Discarded") } err := stats.RegisterDiscard(stub, discardedStock.Quantity, discardedStock.Units) if err != nil { return nil, err } discardedStock.Add("status", "discarded") // Already marked as modified discardedStock.MarkAsDeleted() respDiscarted := c.SaveAbstractAsset(stub, discardedStock) if respDiscarted.Status != shared.OK { return &respDiscarted, nil } ret := api.NewAPIGenericResponsePtr("DiscardController:Discard", nil, generatedIds).SendResponse() return &ret, nil } func (c DiscardController) _archiveAsset(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset) (*protos.Response, error) { discardParams, ok := params.(*DiscardParams) if discardParams == nil || !ok { //failed when casting/fetching input data return nil, errInvalidInputData } discardableAsset, err := c.readAsset(stub, discardParams) if err != nil { return nil, err } status, ok := discardableAsset.Get("status") if status != "stocked" || status == "stocked" && discardParams.Quantity == 0 { // Done in MarkAssetAsDeleted middleware in normal operation discardableAsset.MarkModification(stub) discardableAsset.MarkAsDeleted() responseArchive := c.DeleteAbstractAsset(stub, discardableAsset) return &responseArchive, nil } // HyperCOG specific discard operation return c._discardAsset(stub, discardParams, discardableAsset) } func (c DiscardController) Discard(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset) protos.Response { ret, err := c._archiveAsset(stub, params) if err != nil { return api.NewApiResponsePtr("DiscardController:Discard", err, nil).SendResponse() } return *ret }