github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/test/chaincodes/AuctionApp/table_api.go (about)

     1  /******************************************************************
     2  Copyright IT People Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  
    16  ******************************************************************/
    17  
    18  ///////////////////////////////////////////////////////////////////////
    19  // Author : IT People - Mohan Venkataraman - table API for v1.0
    20  // Purpose: Explore the Hyperledger/fabric and understand
    21  // how to write an chain code, application/chain code boundaries
    22  // The code is not the best as it has just hammered out in a day or two
    23  // Feedback and updates are appreciated
    24  ///////////////////////////////////////////////////////////////////////
    25  
    26  package main
    27  
    28  import (
    29  	"bytes"
    30  	"errors"
    31  	"fmt"
    32  
    33  	"github.com/hyperledger/fabric/core/chaincode/shim"
    34  )
    35  
    36  //////////////////////////////////////////////////////////////////////////////////////////////////
    37  // The recType is a mandatory attribute. The original app was written with a single table
    38  // in mind. The only way to know how to process a record was the 70's style 80 column punch card
    39  // which used a record type field. The array below holds a list of valid record types.
    40  // This could be stored on a blockchain table or an application
    41  //////////////////////////////////////////////////////////////////////////////////////////////////
    42  var recType = []string{"ARTINV", "USER", "BID", "AUCREQ", "POSTTRAN", "OPENAUC", "CLAUC", "XFER", "VERIFY", "DOWNLOAD"}
    43  
    44  //////////////////////////////////////////////////////////////////////////////////////////////////
    45  // The following array holds the list of tables that should be created
    46  // The deploy/init deletes the tables and recreates them every time a deploy is invoked
    47  //////////////////////////////////////////////////////////////////////////////////////////////////
    48  var Objects = []string{"PARTY", "CASHTXN", "User", "UserCat", "Item", "ItemCat", "ItemHistory", "Auction", "AucInit", "AucOpen", "Bid", "Trans"}
    49  
    50  /////////////////////////////////////////////////////////////////////////////////////////////////////
    51  // A Map that holds ObjectNames and the number of Keys
    52  // This information is used to dynamically Create, Update
    53  // Replace , and Query the Ledger
    54  // In this model all attributes in a table are strings
    55  // The chain code does both validation
    56  // A dummy key like 2016 in some cases is used for a query to get all rows
    57  //
    58  //              "User":        1, Key: UserID
    59  //              "Item":        1, Key: ItemID
    60  //              "UserCat":     3, Key: "2016", UserType, UserID
    61  //              "ItemCat":     3, Key: "2016", ItemSubject, ItemID
    62  //              "Auction":     1, Key: AuctionID
    63  //              "AucInit":     2, Key: Year, AuctionID
    64  //              "AucOpen":     2, Key: Year, AuctionID
    65  //              "Trans":       2, Key: AuctionID, ItemID
    66  //              "Bid":         2, Key: AuctionID, BidNo
    67  //              "ItemHistory": 4, Key: ItemID, Status, AuctionHouseID(if applicable),date-time
    68  //
    69  // The additional key is the ObjectType (aka ObjectName or Object). The keys  would be
    70  // keys: {"User", UserId} or keys: {"AuctInit", "2016", "1134"}
    71  /////////////////////////////////////////////////////////////////////////////////////////////////////
    72  
    73  func GetNumberOfKeys(tname string) int {
    74  	ObjectMap := map[string]int{
    75  		"User":        1,
    76  		"Item":        1,
    77  		"UserCat":     3,
    78  		"ItemCat":     3,
    79  		"Auction":     1,
    80  		"AucInit":     2,
    81  		"AucOpen":     2,
    82  		"Trans":       2,
    83  		"Bid":         2,
    84  		"ItemHistory": 4,
    85  		"PARTY":       2,
    86  		"CASHTXN":     1,
    87  	}
    88  	return ObjectMap[tname]
    89  }
    90  
    91  /////////////////////////////////////////////////////////////////
    92  // This function checks the incoming args for a valid record
    93  // type entry as per the declared array recType[]
    94  // The rectType attribute can be anywhere in the args or struct
    95  // not necessarily in args[1] as per my old logic
    96  // The Request type is used to direct processing
    97  // the record accordingly e: recType is "USER"
    98  // "Args":["PostUser","100", "USER", "Ashley Hart", "TRD",  "Morrisville Parkway, #216, Morrisville, NC 27560",
    99  //         "9198063535", "ashley@it people.com", "SUNTRUST", "0001732345", "0234678", "2017-01-02 15:04:05"]}'
   100  /////////////////////////////////////////////////////////////////
   101  func ChkRecType(args []string) bool {
   102  	for _, rt := range args {
   103  		for _, val := range recType {
   104  			if val == rt {
   105  				return true
   106  			}
   107  		}
   108  	}
   109  	return false
   110  }
   111  
   112  /////////////////////////////////////////////////////////////////
   113  // Checks if the incoming invoke has a valid requesType
   114  // The Request type is used to process the record accordingly
   115  // Old Logic (see new logic up)
   116  /////////////////////////////////////////////////////////////////
   117  func CheckRecType(rt string) bool {
   118  	for _, val := range recType {
   119  		if val == rt {
   120  			fmt.Println("CheckRequestType() : Valid Request Type , val : ", val, rt, "\n")
   121  			return true
   122  		}
   123  	}
   124  	fmt.Println("CheckRequestType() : Invalid Request Type , val : ", rt, "\n")
   125  	return false
   126  }
   127  
   128  /////////////////////////////////////////////////////////////////
   129  // Checks if the args contain a valid Record Type. Typically, this
   130  // model expects the Object Type to be args[2] but
   131  // for the sake of flexibility, it scans the input data for
   132  // a valid type if available
   133  /////////////////////////////////////////////////////////////////
   134  func IdentifyRecType(args []string) (string, error) {
   135  	for _, rt := range args {
   136  		for _, val := range recType {
   137  			if val == rt {
   138  				return rt, nil
   139  			}
   140  		}
   141  	}
   142  	return "", fmt.Errorf("IdentifyRecType: Not Found")
   143  }
   144  
   145  /////////////////////////////////////////////////////////////////
   146  // Checks if the args contain a valid Object Type. Typically, this
   147  // model expects the Object Type to be args[0] but
   148  // for the sake of flexibility, it scans the input data for
   149  // a valid type if available
   150  /////////////////////////////////////////////////////////////////
   151  func IdentifyObjectType(args []string) (string, error) {
   152  	for _, rt := range args {
   153  		for _, val := range Objects {
   154  			if val == rt {
   155  				return rt, nil
   156  			}
   157  		}
   158  	}
   159  	return "", fmt.Errorf("IdentifyObjectType: Object Not Found")
   160  }
   161  
   162  ////////////////////////////////////////////////////////////////////////////
   163  // Open a Ledgers if one does not exist
   164  // These ledgers will be used to write /  read data
   165  ////////////////////////////////////////////////////////////////////////////
   166  func InitObject(stub shim.ChaincodeStubInterface, objectType string, keys []string) error {
   167  
   168  	fmt.Println(">> Not Implemented Yet << Initializing Object : ", objectType, " Keys: ", keys)
   169  	return nil
   170  }
   171  
   172  ////////////////////////////////////////////////////////////////////////////
   173  // Update the Object - Replace current data with replacement
   174  // Register users into this table
   175  ////////////////////////////////////////////////////////////////////////////
   176  func UpdateObject(stub shim.ChaincodeStubInterface, objectType string, keys []string, objectData []byte) error {
   177  
   178  	// Check how many keys
   179  
   180  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	// Convert keys to  compound key
   186  	compositeKey, _ := stub.CreateCompositeKey(objectType, keys)
   187  
   188  	// Add Object JSON to state
   189  	err = stub.PutState(compositeKey, objectData)
   190  	if err != nil {
   191  		fmt.Println("UpdateObject() : Error inserting Object into State Database %s", err)
   192  		return err
   193  	}
   194  
   195  	return nil
   196  
   197  }
   198  
   199  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   200  // Retrieve the object based on the key and simply delete it
   201  //
   202  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   203  func DeleteObject(stub shim.ChaincodeStubInterface, objectType string, keys []string) error {
   204  
   205  	// Check how many keys
   206  
   207  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	// Convert keys to  compound key
   213  	compositeKey, _ := stub.CreateCompositeKey(objectType, keys)
   214  
   215  	// Remove object from the State Database
   216  	err = stub.DelState(compositeKey)
   217  	if err != nil {
   218  		fmt.Println("DeleteObject() : Error deleting Object into State Database %s", err)
   219  		return err
   220  	}
   221  	fmt.Println("DeleteObject() : ", "Object : ", objectType, " Key : ", compositeKey)
   222  
   223  	return nil
   224  }
   225  
   226  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   227  // Delete all objects of ObjectType
   228  //
   229  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   230  func DeleteAllObjects(stub shim.ChaincodeStubInterface, objectType string) error {
   231  
   232  	// Convert keys to  compound key
   233  	compositeKey, _ := stub.CreateCompositeKey(objectType, []string{""})
   234  
   235  	// Remove object from the State Database
   236  	err := stub.DelState(compositeKey)
   237  	if err != nil {
   238  		fmt.Println("DeleteAllObjects() : Error deleting all Object into State Database %s", err)
   239  		return err
   240  	}
   241  	fmt.Println("DeleteObject() : ", "Object : ", objectType, " Key : ", compositeKey)
   242  
   243  	return nil
   244  }
   245  
   246  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   247  // Replaces the Entry in the Ledger
   248  // The existing object is simply queried and the data contents is replaced with
   249  // new content
   250  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   251  func ReplaceObject(stub shim.ChaincodeStubInterface, objectType string, keys []string, objectData []byte) error {
   252  
   253  	// Check how many keys
   254  
   255  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   256  	if err != nil {
   257  		return err
   258  	}
   259  
   260  	// Convert keys to  compound key
   261  	compositeKey, _ := stub.CreateCompositeKey(objectType, keys)
   262  
   263  	// Add Party JSON to state
   264  	err = stub.PutState(compositeKey, objectData)
   265  	if err != nil {
   266  		fmt.Println("ReplaceObject() : Error replacing Object in State Database %s", err)
   267  		return err
   268  	}
   269  
   270  	fmt.Println("ReplaceObject() : - end init object ", objectType)
   271  	return nil
   272  }
   273  
   274  ////////////////////////////////////////////////////////////////////////////
   275  // Query a User Object by Object Name and Key
   276  // This has to be a full key and should return only one unique object
   277  ////////////////////////////////////////////////////////////////////////////
   278  func QueryObject(stub shim.ChaincodeStubInterface, objectType string, keys []string) ([]byte, error) {
   279  
   280  	// Check how many keys
   281  
   282  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  
   287  	compoundKey, _ := stub.CreateCompositeKey(objectType, keys)
   288  	fmt.Println("QueryObject() : Compound Key : ", compoundKey)
   289  
   290  	Avalbytes, err := stub.GetState(compoundKey)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	return Avalbytes, nil
   296  }
   297  
   298  ////////////////////////////////////////////////////////////////////////////
   299  // Query a User Object by Object Name and Key
   300  // This has to be a full key and should return only one unique object
   301  ////////////////////////////////////////////////////////////////////////////
   302  func QueryObjectWithProcessingFunction(stub shim.ChaincodeStubInterface, objectType string, keys []string, fname func(shim.ChaincodeStubInterface, []byte, []string) error) ([]byte, error) {
   303  
   304  	// Check how many keys
   305  
   306  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	compoundKey, _ := stub.CreateCompositeKey(objectType, keys)
   312  	fmt.Println("QueryObject: Compound Key : ", compoundKey)
   313  
   314  	Avalbytes, err := stub.GetState(compoundKey)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	if Avalbytes == nil {
   320  		return nil, fmt.Errorf("QueryObject: No Data Found for Compound Key : ", compoundKey)
   321  	}
   322  
   323  	// Perform Any additional processing of data
   324  	fmt.Println("fname() : Successful - Proceeding to fname")
   325  
   326  	err = fname(stub, Avalbytes, keys)
   327  	if err != nil {
   328  		fmt.Println("QueryLedger() : Cannot execute  : ", fname)
   329  		jsonResp := "{\"fname() Error\":\" Cannot create Object for key " + compoundKey + "\"}"
   330  		return Avalbytes, errors.New(jsonResp)
   331  	}
   332  
   333  	return Avalbytes, nil
   334  }
   335  
   336  ////////////////////////////////////////////////////////////////////////////
   337  // Get a List of Rows based on query criteria from the OBC
   338  // The getList Function
   339  ////////////////////////////////////////////////////////////////////////////
   340  func GetKeyList(stub shim.ChaincodeStubInterface, args []string) (shim.StateQueryIteratorInterface, error) {
   341  
   342  	// Define partial key to query within objects namespace (objectType)
   343  	objectType := args[0]
   344  
   345  	// Check how many keys
   346  
   347  	err := VerifyAtLeastOneKeyIsPresent(objectType, args[1:])
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	// Execute the Query
   353  	// This will execute a key range query on all keys starting with the compound key
   354  	resultsIterator, err := stub.GetStateByPartialCompositeKey(objectType, args[1:])
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	defer resultsIterator.Close()
   360  
   361  	// Iterate through result set
   362  	var i int
   363  	for i = 0; resultsIterator.HasNext(); i++ {
   364  
   365  		// Retrieve the Key and Object
   366  		myCompositeKey, err := resultsIterator.Next()
   367  		if err != nil {
   368  			return nil, err
   369  		}
   370  		fmt.Println("GetList() : my Value : ", myCompositeKey)
   371  	}
   372  	return resultsIterator, nil
   373  }
   374  
   375  ///////////////////////////////////////////////////////////////////////////////////////////
   376  // GetQueryResultForQueryString executes the passed in query string.
   377  // Result set is built and returned as a byte array containing the JSON results.
   378  ///////////////////////////////////////////////////////////////////////////////////////////
   379  func GetQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
   380  
   381  	fmt.Println("GetQueryResultForQueryString() : getQueryResultForQueryString queryString:\n%s\n", queryString)
   382  
   383  	resultsIterator, err := stub.GetQueryResult(queryString)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	defer resultsIterator.Close()
   388  
   389  	// buffer is a JSON array containing QueryRecords
   390  	var buffer bytes.Buffer
   391  	buffer.WriteString("[")
   392  
   393  	bArrayMemberAlreadyWritten := false
   394  	for resultsIterator.HasNext() {
   395  		queryResponse, err := resultsIterator.Next()
   396  		if err != nil {
   397  			return nil, err
   398  		}
   399  		// Add a comma before array members, suppress it for the first array member
   400  		if bArrayMemberAlreadyWritten == true {
   401  			buffer.WriteString(",")
   402  		}
   403  		buffer.WriteString("{\"Key\":")
   404  		buffer.WriteString("\"")
   405  		buffer.WriteString(queryResponse.Key)
   406  		buffer.WriteString("\"")
   407  
   408  		buffer.WriteString(", \"Record\":")
   409  		// Record is a JSON object, so we write as-is
   410  		buffer.WriteString(string(queryResponse.Value))
   411  		buffer.WriteString("}")
   412  		bArrayMemberAlreadyWritten = true
   413  	}
   414  	buffer.WriteString("]")
   415  
   416  	fmt.Println("GetQueryResultForQueryString(): getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
   417  
   418  	return buffer.Bytes(), nil
   419  }
   420  
   421  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   422  // Retrieve a list of Objects from the Query
   423  // The function returns an iterator from which objects can be retrieved.
   424  //      defer rs.Close()
   425  //
   426  //      // Iterate through result set
   427  //      var i int
   428  //      for i = 0; rs.HasNext(); i++ {
   429  //
   430  //              // We can process whichever return value is of interest
   431  //              myKey , myKeyVal , err := rs.Next()
   432  //              if err != nil {
   433  //                      return shim.Success(nil)
   434  //              }
   435  //              bob, _ := JSONtoUser(myKeyVal)
   436  //              fmt.Println("GetList() : my Value : ", bob)
   437  //      }
   438  //
   439  // eg: Args":["fetchlist", "PARTY","CHK"]}
   440  // fetchList is the function that calls getList : ObjectType = "Party" and key is "CHK"
   441  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   442  func GetList(stub shim.ChaincodeStubInterface, objectType string, keys []string) (shim.StateQueryIteratorInterface, error) {
   443  
   444  	// Check how many keys
   445  
   446  	err := VerifyAtLeastOneKeyIsPresent(objectType, keys)
   447  	if err != nil {
   448  		return nil, err
   449  	}
   450  
   451  	// Get Result set
   452  	resultIter, err := stub.GetStateByPartialCompositeKey(objectType, keys)
   453  	fmt.Println("GetList(): Retrieving Objects into an array")
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  
   458  	// Return iterator for result set
   459  	// Use code above to retrieve objects
   460  	return resultIter, nil
   461  }
   462  
   463  ////////////////////////////////////////////////////////////////////////////
   464  // This function verifies if the number of key provided is at least 1 and
   465  // < the max keys defined for the Object
   466  ////////////////////////////////////////////////////////////////////////////
   467  
   468  func VerifyAtLeastOneKeyIsPresent(objectType string, args []string) error {
   469  
   470  	// Check how many keys
   471  	nKeys := GetNumberOfKeys(objectType)
   472  	nCol := len(args)
   473  	if nCol == 1 {
   474  		return nil
   475  	}
   476  
   477  	if nCol < 1 {
   478  		error_str := fmt.Sprintf("VerifyAtLeastOneKeyIsPresent() Failed: Atleast 1 Key must is needed :  nKeys : %s, nCol : %s ", nKeys, nCol)
   479  		fmt.Println(error_str)
   480  		return errors.New(error_str)
   481  	}
   482  
   483  	return nil
   484  }