mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-19 08:15:08 +00:00
* Adding JS App workflow, Bugfixes in Go Chaincode Initial impl JS App workflow, Added 2 asset sample, with transfer & delete Bugfixes: Bug fix in ReadTransferAgreement Improved logging in Go Chaincode, Improved Chaincode error handling, checks for execution on orgs peer, bug fix, lint issues use addDiscoveryInterest to scope policy, instead of setEndorsingOrganizations Signed-off-by: Sijo Cherian <sijo@ibm.com> * added js app to CI pipeline , and linting matrix Signed-off-by: Sijo Cherian <sijo@ibm.com> Co-authored-by: Sijo Cherian <sijo@ibm.com>
191 lines
6.8 KiB
Go
191 lines
6.8 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
|
)
|
|
|
|
// ReadAsset reads the information from collection
|
|
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, assetID string) (*Asset, error) {
|
|
|
|
log.Printf("ReadAsset: collection %v, ID %v", assetCollection, assetID)
|
|
assetJSON, err := ctx.GetStub().GetPrivateData(assetCollection, assetID) //get the asset from chaincode state
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read from asset %v", err)
|
|
}
|
|
|
|
//No Asset found, return empty response
|
|
if assetJSON == nil {
|
|
log.Printf("%v does not exist in collection %v", assetID, assetCollection)
|
|
return nil, nil
|
|
}
|
|
|
|
var asset *Asset
|
|
err = json.Unmarshal(assetJSON, &asset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
|
|
}
|
|
|
|
return asset, nil
|
|
|
|
}
|
|
|
|
// ReadAssetPrivateDetails reads the asset private details in organization specific collection
|
|
func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionContextInterface, collection string, assetID string) (*AssetPrivateDetails, error) {
|
|
log.Printf("ReadAssetPrivateDetails: collection %v, ID %v", collection, assetID)
|
|
assetDetailsJSON, err := ctx.GetStub().GetPrivateData(collection, assetID) // Get the asset from chaincode state
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read from asset details %v", err)
|
|
}
|
|
if assetDetailsJSON == nil {
|
|
log.Printf("AssetPrivateDetails for %v does not exist in collection %v", assetID, collection)
|
|
return nil, nil
|
|
}
|
|
|
|
var assetDetails *AssetPrivateDetails
|
|
err = json.Unmarshal(assetDetailsJSON, &assetDetails)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
|
|
}
|
|
|
|
return assetDetails, nil
|
|
}
|
|
|
|
// ReadTransferAgreement gets the buyer's identity from the transfer agreement from collection
|
|
func (s *SmartContract) ReadTransferAgreement(ctx contractapi.TransactionContextInterface, assetID string) (*TransferAgreement, error) {
|
|
log.Printf("ReadTransferAgreement: collection %v, ID %v", assetCollection, assetID)
|
|
// composite key for TransferAgreement of this asset
|
|
transferAgreeKey, err := ctx.GetStub().CreateCompositeKey(transferAgreementObjectType, []string{assetID})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create composite key: %v", err)
|
|
}
|
|
|
|
buyerIdentity, err := ctx.GetStub().GetPrivateData(assetCollection, transferAgreeKey) // Get the identity from collection
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read TransferAgreement: %v", err)
|
|
}
|
|
if buyerIdentity == nil {
|
|
log.Printf("TransferAgreement for %v does not exist", assetID)
|
|
return nil, nil
|
|
}
|
|
agreement := &TransferAgreement{
|
|
ID: assetID,
|
|
BuyerID: string(buyerIdentity),
|
|
}
|
|
return agreement, nil
|
|
}
|
|
|
|
// GetAssetByRange performs a range query based on the start and end keys provided. Range
|
|
// queries can be used to read data from private data collections, but can not be used in
|
|
// a transaction that also writes to private data.
|
|
func (s *SmartContract) GetAssetByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]*Asset, error) {
|
|
|
|
resultsIterator, err := ctx.GetStub().GetPrivateDataByRange(assetCollection, startKey, endKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resultsIterator.Close()
|
|
|
|
results := []*Asset{}
|
|
|
|
for resultsIterator.HasNext() {
|
|
response, err := resultsIterator.Next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var asset *Asset
|
|
err = json.Unmarshal(response.Value, &asset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
|
|
}
|
|
|
|
results = append(results, asset)
|
|
}
|
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
// =======Rich queries =========================================================================
|
|
// Two examples of rich queries are provided below (parameterized query and ad hoc query).
|
|
// Rich queries pass a query string to the state database.
|
|
// Rich queries are only supported by state database implementations
|
|
// that support rich query (e.g. CouchDB).
|
|
// The query string is in the syntax of the underlying state database.
|
|
// With rich queries there is no guarantee that the result set hasn't changed between
|
|
// endorsement time and commit time, aka 'phantom reads'.
|
|
// Therefore, rich queries should not be used in update transactions, unless the
|
|
// application handles the possibility of result set changes between endorsement and commit time.
|
|
// Rich queries can be used for point-in-time queries against a peer.
|
|
// ============================================================================================
|
|
|
|
// ===== Example: Parameterized rich query =================================================
|
|
|
|
// QueryAssetByOwner queries for assets based on assetType, owner.
|
|
// This is an example of a parameterized query where the query logic is baked into the chaincode,
|
|
// and accepting a single query parameter (owner).
|
|
// Only available on state databases that support rich query (e.g. CouchDB)
|
|
// =========================================================================================
|
|
func (s *SmartContract) QueryAssetByOwner(ctx contractapi.TransactionContextInterface, assetType string, owner string) ([]*Asset, error) {
|
|
|
|
queryString := fmt.Sprintf("{\"selector\":{\"objectType\":\"%v\",\"owner\":\"%v\"}}", assetType, owner)
|
|
|
|
queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return queryResults, nil
|
|
}
|
|
|
|
|
|
// QueryAssets uses a query string to perform a query for assets.
|
|
// Query string matching state database syntax is passed in and executed as is.
|
|
// Supports ad hoc queries that can be defined at runtime by the client.
|
|
// If this is not desired, follow the QueryAssetByOwner example for parameterized queries.
|
|
// Only available on state databases that support rich query (e.g. CouchDB)
|
|
func (s *SmartContract) QueryAssets(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
|
|
|
|
queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return queryResults, nil
|
|
}
|
|
|
|
// getQueryResultForQueryString executes the passed in query string.
|
|
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Asset, error) {
|
|
|
|
resultsIterator, err := ctx.GetStub().GetPrivateDataQueryResult(assetCollection, queryString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resultsIterator.Close()
|
|
|
|
results := []*Asset{}
|
|
|
|
for resultsIterator.HasNext() {
|
|
response, err := resultsIterator.Next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var asset *Asset
|
|
|
|
err = json.Unmarshal(response.Value, &asset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
|
|
}
|
|
|
|
results = append(results, asset)
|
|
}
|
|
return results, nil
|
|
}
|