Scaffolded Go Chaincode Project

Blockchain App Builder takes the input from your specification file and generates a fully-functional scaffolded chaincode project. The project contains automatically generated classes and functions, CRUD methods, SDK methods, automatic validation of arguments, marshalling/un-marshalling and transparent persistence capability (ORM).

If the chaincode project is in the Go language, the scaffolded project contains three main files:
  • main.go
  • <chaincodeName>.model.go
  • <chaincodeName>.controller.go
All the necessary libraries are installed and packaged.

The <chaincodeName>.model.go file in the model subdirectory contains multiple asset definitions and the <chaincodeName>.controller.go file in the controller subdirectory contains the asset's behavior and CRUD methods. The various Go struct tags and packages in model.go and controller.go provide support for features like automatic validation of arguments, marshalling/unmarshalling of arguments, transparent persistence capability (ORM) and calling rich queries.

The scaffolded project can be found in $GOPATH/src/example.com/<chaincodeName>

Model

Asset Type Property

By default every struct will have an additional property called AssetType. This property can be useful in fetching only assets of this type. Any changes to this property is ignored during create and update of asset. The property value by default is <modelName>.
type Supplier struct {
AssetType string 'json:"AssetType" default:"TestGoProject.Supplier"'

SupplierId            string      'json:"SupplierId" validate:"string,mandatory" id:"true'
RawMaterialAvailable  int         'json:"RawMaterialAvailable" validate:"int,min=0"'
License               string      'json:"License" validate:"string,min=10"'
ExpiryDate            date.Date   'json:"ExpiryDate" validate:"date,before=2020-06-26"'
Active                bool	  'json:"Active" validate:"bool" default:"true"'
Metadata              interface{} 'json:"Metadata,omitempty"'
}

Validators

Id
id:"true"
This validator identifies the property which uniquely defines the underlying asset. The asset is saved by the value in this key. This validator automatically applies when a new Go project is scaffolded.
In the below screenshot "SupplierId" is the key for the supplier asset and has a tag property id:"true" for the SupplierId property.
type Supplier struct {
    Supplierld              string        'json:"Supplierld" validate:"string,mandatory" id:"true"' 
    RawMaterialAvailable    int           'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                 string        'json:"License" validate:"string,min=10"'
    ExpiryDate              date.Date     'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                  bool          'json:"Active" validate:"bool" default :"true"'
    Metadata                interface{}   'json:"Metadata,omitempty"'   
}
Derived
derived:"strategy,algorithm,format"
This decorator is used for defining the attribute derived from other properties. This decorator has two mandatory parameters:
  • strategy: takes values of concat or hash. Requires an additional parameter algorithm if hash is selected. The default algorithm is sha256; md5 is also supported.
  • format: takes an array of specification strings and values to be used by the strategy.
type Supplier struct{
   AssetType string 'json:"AssetType" final:"chaincode1.Supplier"'
   SupplierId   string  'json:"SupplierId" validate:"string" id:"true" mandatory:"true" derived:"strategy=hash,algorith=sha256,format=IND%1%2,License,Name"'
   Name         string  'json:"Name" validate:"string,min=2,max=4"'
   License      string  'json:"License" validate:"string,min=2,max=4"'
}
Mandatory
validate:"mandatory"
This marks the following property as mandatory and cannot be skipped while saving to the ledger. If skipped it throws an error. In the below example, "SupplierId" has a validate:"mandatory" tag.
Type Supplier struct {
    Supplierld              string      'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable    int         'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                 string      'json:"License" validate:"string,min=10"'
    ExpiryDate              date.Date   'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                  bool        'json:"Active" validate:"bool" default :"true"'
    Metadata                interface{} 'json:"Metadata,omitempty"'
}
Default
default:"<param>"
This states that the following property can have a default value. The default value in the default tag is used when the property is skipped while saving to the ledger. In the below example property, Active has a default value of true, provided as tag default:"true"
Type Supplier struct {
    Supplierld            string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License               string       'json:"License" validate:"string,min=10"'
    ExpiryDate            date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                bool         'json:"Active" validate:"bool" default :"true"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
Validate types
Basic Go types are validated for a property by defining a validate tag. These are the validate tags based on types:
  • string: validate: "string"
  • date: validate: "date"
  • number: validate: "int"
  • boolean: validate: "bool"
Min validator
validate:"min=<param>"
Using the min validator, minimum value can be set for a property of type number and string.
For type int: In the example, RawMaterialAvailable property has a minimum value of 0 and if a value less than 0 is applied to RawMaterialAvailable an error will be returned.
For type string: For the string type minimum validator will check the length of the string with the provided value. Therefore, in the below example the License property has to be minimum 10 characters long.
Example:
Type Supplier struct {
    Supplierld             string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable   int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                string       'json:"License" validate:"string,min=10"'
    ExpiryDate             date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                 bool         'json:"Active" validate:"bool" default :"true"'
    Metadata               interface{}  'json:"Metadata,omitempty"'
}
Max validator
validate:"max=<param>"
Using the max validator, the maximum value can be set for a property of type number and string.
For type int: Like the min validator, for type int, if a value provided for the structfield is greater than the value provided in the validator then an error will be returned.
For type string: Like the min validator, max validator will also check the length of the string with given value. In the example, the Domain property has a maximum value of 50, so if the Domain property has a string length more than 50 characters, then an error message will be returned.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}
Date validators
Before validator:
validate:"before=<param>"
The before validator validates a property of type date to have a value less than the specified in parameter.
In this example, the ExpiryDate property should be before "2020-06-26" and if not it will return an error.
Type Supplier struct {
    Supplierld            string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License               string       'json:"License" validate:"string,min=10"'
    ExpiryDate            date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                bool         'json:"Active" validate:"bool" default :"true"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
After validator:
validate:"after=<param>"
The before validator validates a property of type date to have a value greater than the specified in parameter.
In this example, the CompletionDate property should be after "2020-06-26" and if not it will return an error.
Type Supplier struct {
    Manufacturerld        string       'json:"Manufacturerld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,max=8"'
    ProductsAvailable     int          'json:"ProductsAvailable" validate:"int"'
    CompletionDate        date.Date    'json:"CompletionDate" validate:"date,after=2020-06-26"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
URL validator
validate:"url"
The URL validator will validate a property for URL strings.
In this example, the Domain property has to be a valid URL.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"string,url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}
Regexp validator
validate:"regexp=<param>"
Regexp validator will validate property for the input regular expression.
In this example, the PhoneNumber property will validate for a mobile number as per the regular expression.
type Customer struct {
Customerld      string       'json:"Customerld" validate:"string,mandatory" id:"true"'
Name            string       'json:"Name" validate:"string,mandatory"'
ProductsBought  int          'json:"ProductsBought" validate:"int"'
OfferApplied    int          'json:"OfferApplied" validate :"int,nax=0"'
PhoneNumber     string       'json:"PhoneNumber" validate:"string,regexp=A\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"'
Received        bool         'json:"Received" validate:"bool"'
Metadata        interface{}  'json:"Metadata,omitempty"'
}
Multiple validators
Multiple validators can be applied a property.
In this example, the Domain property has validation for a string, URL, and min and max string length.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"string,url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}

ORM

Transparent Persistence Capability or simplified ORM is captured in the Model class of the Context (Ctx) object. If your model calls any of the following SDK methods, access them by using t.Ctx.Model.

SDK methods that implement ORM are the following methods:
  • Save – this calls the Hyperledger Fabric PutState method
  • Get – this calls the Hyperledger Fabric GetState method
  • Update – this calls the Hyperledger Fabric PutState method
  • Delete – this calls the Hyperledger Fabric DeleteState method
  • History – this calls the Hyperledger Fabric GetHistoryForKey method
  • GetByRange – this calls the Hyperledger Fabric GetStateByRange method
  • GetByRangeWithPagination – this calls the Hyperledger Fabric GetStateByRangeWithPagination method

SDK Methods

Go chaincodes implement Transparent Persistence Capability (ORM) with the model package.

Note:

Beginning with version 21.2.3, the way to access the ORM methods has changed. Run the ochain --version command to determine the version of Blockchain App Builder.

In previous releases, the ORM methods were exposed as static methods in the model package. The methods are now defined on the model receiver, which holds the transaction stub. To call these methods, you use the model receiver held by the transaction context in the controller. You call these methods as t.Ctx.Model.<method_name> instead of model.<method_name>.

The following example shows Save and Get method calls in previous releases:

func (t *Controller) CreateSupplier(asset Supplier) (interface{}, error) {
  return model.Save(&asset)
}

func (t *Controller) GetSupplierById(id string) (Supplier, error) {
  var asset Supplier
  _, err := model.Get(id, &asset)
  return asset, err
}

The following example shows Save and Get method calls from the version 21.2.3 and later:

func (t *Controller) CreateSupplier(asset Supplier) (interface{}, error) {
  return t.Ctx.Model.Save(&asset)
}

func (t *Controller) GetSupplierById(id string) (Supplier, error) {
  var asset Supplier
  _, err := t.Ctx.Model.Get(id, &asset)
  return asset, err
}

After you upgrade to version 21.2.3, make this change in all chaincode projects that you created with an earlier version of Blockchain App Builder. If you use the sync command to synchronize changes between the specification file and your source code, the changes are automatically brought to your controller for the ready-to-use methods. You still need to manually resolve any conflicts.

The following ORM methods are exposed via the model package:

Get
Queries the ledger for the stored asset based on the given ID.
func Get(Id string, result ...interface{}) (interface{}, error)
Parameters:
  • Id - The ID of the asset which is required from the ledger.
  • result (interface{}) - This is an empty asset object of a particular type, which is passed by reference. This object will contain the result from this method. To be used only if type-specific result is required.
  • asset (interface) - Empty asset object, which is passed by reference. This object will contain the result from this method. To be used only if type-specific result is required.
Returns:
  • interface {} - Interface contains the asset in the form of map[string]interface{}. Before operating on this map, it is required to assert the obtained interface with type map[string]interface{}. To convert this map into an asset object, you can use the utility API util.ConvertMaptoStruct (see: Utility Package).
  • error - Contains an error if returned, or is nil.
Update
Updates the provided asset in the ledger with the new values.
func Update(args ...interface{}) (interface{}, error)
Parameters:
  • obj (interface) - The object that is required to be updated in the ledger is passed by reference into this API with the new values. The input asset is validated and verified according to the struct tags mentioned in the model specification and then stored into the ledger.
Returns:
  • interface{} - The saved asset is returned as an interface.
  • error - Contains an error if returned, or is nil.
Save
Saves the asset to the ledger after validating on all the struct tags.
func Save(args ...interface{}) (interface{}, error)
Parameters:
  • obj/args[0] (interface{}) - The object that needs to be stored in the ledger is passed by reference in this utility method.
  • metadata/args[1] (interface{}) - This parameter is optional. It has been given in order to facilitate you if you're required to store any metadata into the ledger along with the asset at the runtime. This parameter can be skipped if no such requirement exists.
Returns:
  • interface {} - The asset is returned as an interface.
  • error - Contains an error if returned, or is nil.
Delete
Deletes the asset from the ledger.
func Delete(Id string) (interface{}, error)
Parameters:
  • id (string) - The ID of the asset which is required to be deleted from the ledger.
Returns:
  • interface {} - Contains the asset being deleted in the form of map[string]interface{}.
GetByRange
Returns the list of assets by range of IDs.
func GetByRange(startKey string, endKey string, asset ...interface{})
([]map[string]interface{}, error)
Parameters:
  • startkey (string) - Starting ID for the range of objects which are required.
  • endkey (string) - End of the range of objects which are required.
  • asset interface - (optional) Empty array of assets, which is passed by reference. This array will contain the result from this method. To be used if type-specific result is required.
Returns:
  • []map[string]interface{} - This array contains the list of assets obtained from the ledger. You can access the objects iterating over this array and asserting the objects as map[string]interface{} and using utility to convert to asset object.
  • error - Contains an error if returned, or is nil.
GetByRangeWithPagination
The GetByRangeWithPagination method is a static method of OchainModel class which is inherited by the concrete Model classes of {chaincodeName}.model.ts.
This returns a list of asset between the range startId and endId, filtered by page size and bookmark. This method calls the Hyperledger Fabric GetStateByRangeWithPagination method internally.
If the modelName parameter is not provided, the method returns Promise<Object [ ] >. If the modelName parameter is provided, then the method handles casting into the caller Model type. In the following example, the result array is of the type Supplier. If the asset returned from the ledger is not of the Model type, then it will not be included in the list. This check is done by the read-only assetType property in the Model class.
To return all the assets between the range startId and endId, filtered by page size and bookmarks, use the generic controller method getAssetsByRange.
func (m *Model) GetByRangeWithPagination(startKey string, endKey string, pageSize int32, bookmark string, asset ...interface{}) ([]map[string]interface{}, error) 
Parameters:
  • startkey : string – Starting key of the range. Included in the range.
  • endkey : string – Ending key of the range. Excluded from the range.
  • pageSize : number – The page size of the query.
  • Bookmark : string – The bookmark of the query. Output starts from this bookmark.
  • asset interface – (Optional) An empty array of assets, passed by reference. This array will contain the result from this method. Use this parameter to get type-specific results.
Returns:
  • []map[string]interface{} – An array that contains the list of assets retrieved from the ledger. You can access the objects by iterating over this array and asserting the objects as map[string]interface{} and using a utility for conversion to an asset object.
  • error – Contains an error if an error is returned, otherwise nil.
GetHistoryById
Returns the history of the asset with the given ID.
func GetHistoryByID(Id string) ([]interface{}, error)
Parameters:
  • Id (string) - ID of the asset for which the history is needed.
Returns:
  • []interface{} - This slice contains the history of the asset obtained from the ledger in form of slice of map[string]interface{}. You can access each history element by iterating over this slice and asserting the objects as map[string]interface{} and using utility to convert to asset object.
  • error - Contains the error if observed.
Query
The query method will run a SQL/Couch DB query over the ledger. This method is only supported for remote deployment on Oracle Blockchain Platform. This is a generic method for executing SQL queries on the ledger.
func Query(queryString string) ([]interface{}, error)
Parameters:
  • queryString (string) - Input the query string.
Returns:
  • []interface{} - This will contain the output of the query. The result is in form of slice of interfaces. You need to iterate over the slice and use the elements by converting them to proper types.
  • error - Contains the error if observed.
QueryWithPagination
The query method will run a SQL/Couch DB query over the ledger, filtered by page size and bookmark. This method is only supported for remote deployment on Oracle Blockchain Platform. This is a generic method for executing SQL queries on the ledger.
func (m *Model) QueryWithPagination(queryString string, pageSize int32, bookmark string) ([]interface{}, error)
Parameters:
  • queryString (string) - Rich SQL/Couch DB query.
  • pageSize : number - The page size of the query.
  • bookmark : string - The bookmark of the query. Output starts from this bookmark.
Returns:
  • []interface{} - This will contain the output of the query. The result is in form of slice of interfaces. You need to iterate over the slice and use the elements by converting them to proper types.
  • error - Contains the error if observed.
InvokeCrossChaincode
You can use this method in a chaincode to call a function in another chaincode. Both chaincodes must be installed on the same peer.
func InvokeCrossChaincode(chaincodeName string, method string, args []string, channelName string) (interface{}, error)
Parameters:
  • chaincodeName – The name of the chaincode to call.
  • methodName - The name of the method to call in the chaincode.
  • arg - The argument of the calling method.
  • channelName - The channel where the chaincode to call is located.
Returns:
  • interface{} - Returns a map[string]interface{} object that contains three keys:
    • isValid - true if the call is valid.
    • payload - The output returned by the cross-chaincode call, as a JSON object.
    • message - The message returned by the cross-chaincode call, in UTF-8 format.
Return Value Example:
{
      "isValid": true,
      "message": "Successfully invoked method [CreateAccount] on sub-chaincode [erc721_go_453]",
      "payload": {
                  "AccountId": "oaccount~6b83b8ab931f99442897dd04cd7a2a55f808686f49052a40334afe3753fda4c4",
                  "AssetType": "oaccount",
                  "BapAccountVersion": 0,
                  "NoOfNfts": 0,
                  "OrgId": "appdev",
                  "TokenType": "nonfungible",
                  "UserId": "user2"
      }
}
InvokeChaincode
You can use this method in a chaincode to call a function in another chaincode. Both chaincodes must be installed on the same peer.
func InvokeChaincode(chaincodeName string, method string, args []string, channelName string) (interface{}, error)
Parameters:
  • chaincodeName – The name of the chaincode to call.
  • methodName - The name of the method to call in the chaincode.
  • arg - The argument of the calling method.
  • channelName - The channel where the chaincode to call is located.
Returns:
  • interface{} - Returns a map[string]interface{} object that contains three keys:
    • isValid - true if the call is valid.
    • payload - The output returned by the cross-chaincode call, in UTF-8 format.
    • message - The message returned by the cross-chaincode call, in UTF-8 format.
Return Value Example:
{
    "isValid": true,
    "message": "Successfully invoked method [CreateAccount] on sub-chaincode [erc721_go_453]",
    "payload": "{\"AssetType\":\"oaccount\",\"AccountId\":\"oaccount~c6bd7f8dcc339bf7144ea2e1cf953f8c1df2f28482b87ad7895ac29e7613a58f\",\"UserId\":\"user1\",\"OrgId\":\"appdev\",\"TokenType\":\"nonfungible\",\"NoOfNfts\":0,\"BapAccountVersion\":0}"
} 

Composite Key Methods

GenerateCompositeKey
This method generates and returns the composite key based on the indexName and the attributes given in the arguments.
func GenerateCompositeKey(indexName string, attributes []string)
(string, error)
Parameters:
  • indexName (string) - Object type of the composite key.
  • attrbutes ([]string) - Attributes of the asset based on which the composite key has to be formed.
Returns:
  • string - This contains the composite key result.
  • error - Contains the error if observed.
GetByCompositeKey
This method returns the asset that matches the key and the column given in the parameters. The index parameter indicates the index of the key returned in the array of stub method SplitCompositeKey.
Internally this method calls Hyperledger Fabric's getStateByPartialCompositeKey, splitCompositeKey and getState.
func GetByCompositeKey(key string, columns []string, index int)
(interface{}, error)
Parameters:
  • key (string) - Object type provided while creating composite key.
  • column ([]string) - This is the slice of attributes on which the ledger has to be queried using the composite key.
  • index(int) - Index of the attribute.
Returns:
  • Interface{} - Contains the list of assets which are result of this method.
  • error - Contains any errors if present.

Stub Method

GetNetworkStub
This method will return the Hyperledger Fabric chaincodeStub.
You can get access to the shim stub by calling the GetNetworkStub method. This will help you write your own implementation working directly with the assets.
func GetNetworkStub() shim.ChaincodeStubInterface
Parameters:
  • none
Returns:
  • shim.ChaincodeStubInterface - This is the Hyperledger Fabric chaincode stub.

Other Methods

  • GetTransactionId()
  • GetTransactionTimestamp()
  • GetChannelID()
  • GetCreator()
  • GetSignedProposal()
  • GetArgs()
  • GetStringArgs()
  • GetCreatorMspId()
  • GetId
GetTransactionId
Returns the transaction ID for the current chaincode invocation request. The transaction ID uniquely identifies the transaction within the scope of the channel.
func GetTransactionId() string
Parameters:
  • none
Returns:
  • string - This contains the required transaction ID.
GetTransactionTimestamp
Returns the timestamp when the transaction was created. This is taken from the transaction ChannelHeader, therefore it will indicate the client's timestamp, and will have the same value across all endorsers.
func GetTransactionTimestamp() (*timestamp.Timestamp, error)
Parameters:
  • none
Returns:
  • timestamp.Timestamp - Contains the timestamp required.
  • error - Contains any errors if present.
GetChannelID
Returns the channel ID for the proposal for the chaincode to process.
func GetChannelID() string
Parameters:
  • none
Returns:
  • string - Contains the required channel ID as a string.
GetCreator
Returns the identity object of the chaincode invocation's submitter
func GetCreator() ([]byte, error)
Parameters:
  • none
Returns:
  • []byte - Contains the required identity object serialized.
  • error - Contains any errors if present.
GetSignedProposal
Returns a fully decoded object of the signed transaction proposal.
func GetSignedProposal() (*peer.SignedProposal, error)
Parameters:
  • none
Returns:
  • *peer.SignedProposal - Contains the required signed proposal object.
  • error - Contains any errors if present.
GetArgs
Returns the arguments as array of strings from the chaincode invocation request.
func GetArgs() [][]byte
Parameters:
  • none
Returns:
  • [][]byte - Contains the arguments passed.
GetStringArgs
Returns the arguments intended for the chaincode Init and Invoke as a string array.
func GetStringArgs() []string
Parameters:
  • none
Returns:
  • []string - Contains the required arguments as a string array.
GetCreatorMspId
Returns the MSP ID of the invoking identity.
func GetCreatorMspId() string
Parameters:
  • none
Returns:
  • string - Returns the MSP ID of the invoking identity.
GetId
When the asset has a derived key as Id, you can use this method to get a derived ID. This method will return an error if the derived key contains %t (timestamp).
Parameters:
  • object - Object should contain all the properties on which the derived key is dependent.
Returns:
  • Returns the derived key as a string.
Example:
func (t *Controller) CustomGetterForSupplier(License string, Name string)(interface{}, error){
   var asset Supplier
   asset.License = License
   asset.Name = Name
   id,err := t.Ctx.Model.GetId(&asset)

   if err !=nil {
      return nil, fmt.Errorf("error in getting ID %v", err.Error())
   }
   return t.GetSupplierById(id)
}

Utility Package

The following methods in the utility package may be useful:

Util.CreateModel
Parses the provided JSON string and creates an asset object of the provided type.
func CreateModel(obj interface{}, inputString string) error
Parameters:
  • inputString (string) - The input JSON string from which the object is to be created.
  • obj (interface{}) - The reference of the object that is to be created from the JSON string. This object will store the created model which is also validated as per validator tags.
Returns:
  • error - Contains any errors found while creating or validating the asset.
util.ConvertMapToStruct
Convert the provided map into object of provided type.
func ConvertMapToStruct(inputMap map[string](interface{}), resultStruct
interface{}) error
Parameters:
  • inputMap (map[string](interface{})) - Map which needs to be converted into the asset object.
  • resultStruct (interface{}) - The reference of the required asset object which needs to be generated from the map. Contains the result asset object required.
Returns:
  • error - Contains any errors found while creating or validating the asset.

For token SDK methods, see the topics under Tokenization Support Using Blockchain App Builder.

Controller

The Controller.go file implements the CRUD and custom methods for the assets.

You can create any number of classes, functions, or files, but only those methods that are defined on chaincode struct are invokable from outside, the rest of them are hidden.

Automatically Generated Methods

As described in Input Specification File, you can specify which CRUD methods you want generated in the specification file. For example, if you selected to generate all methods, the result would be similar to:

//	
//Supplier
//	
func (t *ChainCode) CreateSupplier(inputString string) (interface{}, error) {
    var obj Supplier
    err := util.CreateModel(&obj, inputString)
    if err != nil {
        return nil, err   
    }
    return model.Save(&obj)
}

func (t *ChainCode) GetSupplierById(id string) (interface{}, error) {
    asset, err := model.Get(id) 
    return asset, err
}

func (t *ChainCode) UpdateSupplier(inputString string) (interface{}, error) {
    var obj Supplier
    err := util.CreateModel(&obj, inputstring)
    if err != nil {   
        return nil, err
    }
return model.Update(&obj)
}

func (t *ChainCode) DeleteSupplier(id string) (interface{}, error) { 
    return model.Delete(id)
}

func (t *ChainCode) GetSupplierHistoryById(id string) (interface{}, error) { 
    historyArray, err := model.GetHistoryByld(id) 
    return historyArray, err
}

func (t *ChainCode) GetSupplierByRange(startkey string, endKey string) (interface{}, error) { 
    assetArray, err := model.GetByRange(startkey, endKey) 
    return assetArray, err
}

Custom Methods

The following custom methods were generated from our example specification file.

The executeQuery shows how SQL rich queries can be called. The validators against the arguments are added automatically by Blockchain App Builder based on the type of the argument specified in the specification file.

You can implement the functionality according to the business logic. If you add custom methods, add them to the controller file. If you add custom methods to the library instead of the controller file, your changes will be lost when the library folder contents are updated during the synchronization or chaincode upgrade processes.

//	
//Custom Methods
//			
/*
*	BDB sql rich queries can be executed in OBP CS/EE.
*	This method can be invoked only when connected to remote OBP CS/EE network.
*/
func (t *ChainCode) ExecuteQuery(inputQuery string) (interface{}, error) { 
    resultArray, err := model.Query(inputQuery) 
    return resultArray, err
}

func (t *ChainCode) FetchRawMaterial(supplierId string, rawMaterialSupply int) (interface{}, error) {
    return nil, nil
}

func (t *ChainCode) GetRawMaterialFromSupplier(manufacturerId string, supplierId string, rawMaterialSupply int) (interface{} error) { 
    return nil, nil
}

Func (t *ChainCode) CreateProducts(manufacturerId string, rawMaterialConsumed int, productsCreated int) (interface{}, error) { 
    return nil, nil
}

func (t *ChainCode) SendProductsToDistribution() (interface{}, error) { 
    return nil, nil
}
For Go chaincodes, every custom method should return two values: empty interface, error. For example:
func (t *Controller) FetchRawMaterial(supplierId string, rawMaterialSupply int) (interface{}, error) { 
    return nil, nil
}

Init Method

A custom Init method is provided in the controller with an empty definition. If you use Blockchain App Builder to deploy or upgrade, the Init method is called automatically. If you deploy or upgrade from the Oracle Blockchain Platform console on the Hyperledger Fabric v1.4.7 platform, the Init method is also called automatically. If you deploy or upgrade from the Oracle Blockchain Platform console on the Hyperledger Fabric v2.x platform, you must call the Init method manually. You can use a third-party tool such as Postman to call the Init method manually.

type Controller struct {
}
func (t *Controller) Init(args string) (interface{}, error) 
    { return nil, nil
}

If you would like to initialize any application state at this point, you can use this method to do that.