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 }