fabric-samples/asset-transfer-private-data/chaincode-go/private_asset_queries.go
Sijo Cherian e90554f4f8
JS App workflow for private-data asset transfer, Chaincode fixes (#252)
* 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>
2020-08-06 14:23:11 -04:00

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
}