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 uses the Go language, the scaffolded project contains three main files:
  • main.go
  • <chaincodeName>.model.go
  • <chaincodeName>.controller.go
All of 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 such as automatic validation of arguments, marshalling/unmarshalling of arguments, transparent persistence capability (ORM), and calling rich queries.

The scaffolded project is located in the $GOPATH/src/example.com/<chaincodeName> directory.

Model

Asset Type Property

By default every struct has an additional property called AssetType. You can use this property to fetch only assets of this type. Any changes to this property are ignored when assets are created or updated. 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 that uniquely defines the underlying asset. The asset is saved by the value in this key. This validator is automatically applied when a new Go project is scaffolded.
In the following example, 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. If hash is specified, the additional parameter algorithm is required. 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 decorator marks the following property as mandatory, so that it cannot be skipped while saving to the ledger. If skipped, an error is thrown. In the following example, the SupplierId property 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 decorator indicates that the following property has a default value. The value in the default tag is used when the property is skipped while saving to the ledger. In the following example, Active has a default value of true, specified as the 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. The following validate tags are based on the basic types:
  • string: validate: "string"
  • date: validate: "date"
  • number: validate: "int"
  • boolean: validate: "bool"
Min validator
validate:"min=<param>"
You can use the min validator to set the minimum value for a property of type number or string.
For type int: In the following example, the RawMaterialAvailable property has a minimum value of 0. If a value less than 0 is applied to the RawMaterialAvailable property, an error will be returned.
For type string: The minimum validator checks the length of the string against the specified value. In the following example, the License property must be at least ten characters long.
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>"
You can use the max validator to set the maximum value for a property of type number and string.
For type int: Similar to the min validator, if a value specified for a struct field is greater than the value provided in the validator, an error is returned.
For type string: Similar to the min validator, the max validator checks the length of the string against the given value. In the following example, the Domain property has a maximum value of 50, so if the Domain property has a string length of more than 50 characters, 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 parameter.
In the following example, the ExpiryDate property must be before "2020-06-26" and if it is not, an error is returned.
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 after validator validates a property of type date to have a value greater than the specified parameter.
In the following example, the CompletionDate property must 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 validates a property for URL strings.
In the following example, the Domain property must 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>"
The regexp validator validates a property with the specified regular expression.
In the following example, the PhoneNumber property will be validated for a mobile number in accordance with 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 the following example, the Domain property has validation for a string, a URL, and minimum and maximum 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.

The following SDK methods implement ORM:
  • Save calls the Hyperledger Fabric PutState method.
  • Get calls the Hyperledger Fabric GetState method.
  • Update calls the Hyperledger Fabric PutState method.
  • Delete calls the Hyperledger Fabric DeleteState method.
  • History calls the Hyperledger Fabric GetHistoryForKey method.
  • GetByRange calls the Hyperledger Fabric GetStateByRange method.
  • GetByRangeWithPagination 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 must still manually resolve any conflicts.

The following ORM methods are exposed by 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 to retrieve from the ledger.
  • result (interface{}) - An empty asset object of a particular type, which is passed by reference. This object will contain the result from this method. Use this parameter only if a type-specific result is required.
  • asset (interface) - An empty asset object, which is passed by reference. This object will contain the result from this method. Use this parameter only if a type-specific result is required.
Returns:
  • interface {} - The interface contains the asset in map[string]interface{} form. Before operating on this map, you must assert the returned 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 specified asset in the ledger with new values.
func Update(args ...interface{}) (interface{}, error)
Parameters:
  • obj (interface) - The object to update in the ledger is passed by reference into this API along with the new values. The input asset is validated and verified according to the struct tags 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 to store in the ledger is passed by reference in this utility method.
  • metadata/args[1] (interface{}) - (Optional) You can use this parameter to store metadata to the ledger along with the asset at runtime.
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 to delete from the ledger.
Returns:
  • interface {} - Contains the asset being deleted in map[string]interface{} form.
GetByRange
Returns a list of assets specified by a 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 to retrieve.
  • endkey (string) - End ID for the range of objects to retrieve.
  • asset interface - (Optional) An empty array of assets, passed by reference. This array will contain the method result. Use this parameter if a 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 by iterating over this array and asserting the objects as map[string]interface{} and using a utility to convert to an 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 method returns a list of assets 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, which is included in the range.
  • endkey : string – Ending key of the range, which is 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.
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 a utility to convert to asset object.
  • error - Contains the error if an error is returned.
Query
This method runs a SQL/Couch DB query over the ledger. This method is supported only for remote deployments on Oracle Blockchain Platform. This is a generic method for running SQL queries on the ledger.
func Query(queryString string) ([]interface{}, error)
Parameters:
  • queryString (string) - The query string.
Returns:
  • []interface{} - The query output in the form of a slice of interfaces. Iterate over the slice and use the elements by converting them to their proper types.
  • error - Contains the error if an error is returned.
QueryWithPagination
This method runs a SQL/Couch DB query over the ledger, filtered by page size and bookmark. This method is supported only for remote deployments on Oracle Blockchain Platform. This is a generic method for running 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{} - The query output in the form of a slice of interfaces. Iterate over the slice and use the elements by converting them to their proper types.
  • error - Contains the error if an error is returned.
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 will be formed.
Returns:
  • string - The composite key result.
  • error - Contains the error if an error is returned.
GetByCompositeKey
This method returns the asset that matches the specified key and column. 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 methods.
func GetByCompositeKey(key string, columns []string, index int)
                (interface{}, error)
Parameters:
  • key (string) - Object type provided while creating composite key.
  • column ([]string) - The slice of attributes on which the ledger will be queried using the composite key.
  • index(int) - Index of the attribute.
Returns:
  • Interface{} - The list of matching assets.
  • error - Contains the error if an error is returned.

Stub Method

GetNetworkStub
This method returns the Hyperledger Fabric chaincodeStub value.
You can access the shim stub by calling the GetNetworkStub method. This can assist you in writing your own implementation that works directly with assets.
func GetNetworkStub() shim.ChaincodeStubInterface
Parameters:
  • none
Returns:
  • shim.ChaincodeStubInterface - The Hyperledger Fabric chaincode stub.

Other Methods

  • GetTransactionId()
  • GetTransactionTimestamp()
  • GetChannelID()
  • GetCreator()
  • GetSignedProposal()
  • GetArgs()
  • GetStringArgs()
  • GetCreatorMspId()
  • GetId
GetTransactionId
This method returns the transaction ID for the current chaincode invocation request. The transaction ID uniquely identifies the transaction in the scope of the channel.
func GetTransactionId() string
Parameters:
  • none
Returns:
  • string - The transaction ID.
GetTransactionTimestamp
Returns the time stamp when the transaction was created. Because the value is taken from the transaction ChannelHeader, it indicates the client's time stamp and has the same value across all endorsers.
func GetTransactionTimestamp() (*timestamp.Timestamp, error)
Parameters:
  • none
Returns:
  • timestamp.Timestamp - The time stamp.
  • error - Contains the error if an error is returned.
GetChannelID
Returns the channel ID for the proposal for the chaincode to process.
func GetChannelID() string
Parameters:
  • none
Returns:
  • string - The requested channel ID in string format.
GetCreator
Returns the identity object of the chaincode invocation's submitter.
func GetCreator() ([]byte, error)
Parameters:
  • none
Returns:
  • []byte - The required identity object in serialized form.
  • error - Contains the error if an error is returned.
GetSignedProposal
Returns a fully decoded object of the signed transaction proposal.
func GetSignedProposal() (*peer.SignedProposal, error)
Parameters:
  • none
Returns:
  • *peer.SignedProposal - The signed proposal object.
  • error - Contains the error if an error is returned.
GetArgs
Returns the arguments as an array of strings from the chaincode invocation request.
func GetArgs() [][]byte
Parameters:
  • none
Returns:
  • [][]byte - The passed arguments.
GetStringArgs
Returns the arguments intended for the chaincode Init and Invoke methods as a string array.
func GetStringArgs() []string
Parameters:
  • none
Returns:
  • []string - The arguments as a string array.
GetCreatorMspId
Returns the MSP ID of the invoking identity.
func GetCreatorMspId() string
Parameters:
  • none
Returns:
  • string - 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 returns an error if the derived key contains %t (time stamp).
Parameters:
  • object - The object must contain all of 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 are provided in the utility package.

Util.CreateModel
Parses the specified JSON string and creates an asset object of the specified type.
func CreateModel(obj interface{}, inputString string) error
Parameters:
  • inputString (string) - The input JSON string to use to create the object.
  • obj (interface{}) - The reference of the object to create from the JSON string. This object will store the created model, which is also validated in accordance with validator tags.
Returns:
  • error - Contains the error if an error is returned while creating or validating the asset.
util.ConvertMapToStruct
Convert the specified map into an object of the specified type.
func ConvertMapToStruct(inputMap map[string](interface{}), resultStruct
interface{}) error
Parameters:
  • inputMap (map[string](interface{})) - The map to convert into an asset object.
  • resultStruct (interface{}) - The reference of the asset object to create from the map.
Returns:
  • error - Contains the error if an error is returned 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 chose to generate all methods, the result would look similar to the following code:

//	
//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 the example specification file.

The executeQuery function 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 functionality according to the necessary 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 must return two values: an empty interface and an error, as shown in the following 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, 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
}

You can then use this method to initialize any application state.