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:
outputclass="language-go"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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- Returns:
[]string
- Contains the required arguments as a string array.
-
GetCreatorMspId
- Returns the MSP ID of the invoking identity.
-
func GetCreatorMspId() string
- Parameters:
- 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, 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 use this method to initialize any application state at this point.