diff --git a/asset-transfer-basic/application-javascript/app.js b/asset-transfer-basic/application-javascript/app.js index b060133a..b307ec83 100644 --- a/asset-transfer-basic/application-javascript/app.js +++ b/asset-transfer-basic/application-javascript/app.js @@ -9,7 +9,7 @@ const {Gateway, Wallets} = require('fabric-network'); const FabricCAServices = require('fabric-ca-client'); const path = require('path'); -const {buildCAClient, registerUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js'); +const {buildCAClient, registerAndEnrollUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js'); const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js'); const channelName = 'mychannel'; @@ -22,25 +22,46 @@ function prettyJSONString(inputString) { } // pre-requisites: -// - fabric-sample two organization test-network setup with two peers, ordering service, and 2 certificate authorities +// - fabric-sample two organization test-network setup with two peers, ordering service, +// and 2 certificate authorities // ===> from directory /fabric-samples/test-network -// network.sh run createChannel -ca -// - any of the asset-transfer-basic chaincodes deployed on the channel "mychannel" with the chaincodeName of "basic" -// This deploy command will package, install, approve, and commit the javascript chaincode, all the actions it takes +// ./network.sh up createChannel -ca +// - Use any of the asset-transfer-basic chaincodes deployed on the channel "mychannel" +// with the chaincode name of "basic". The following deploy command will package, +// install, approve, and commit the javascript chaincode, all the actions it takes // to deploy a chaincode to a channel. // ===> from directory /fabric-samples/test-network -// network.sh deployCC -ccn basic -ccl javascript -// - node install +// ./network.sh deployCC -ccn basic -ccl javascript +// - Be sure that node.js is installed +// ===> from directory /fabric-samples/asset-transfer-basic/application-javascript +// node -v // - npm installed code dependencies // ===> from directory /fabric-samples/asset-transfer-basic/application-javascript // npm install // - to run this test application // ===> from directory /fabric-samples/asset-transfer-basic/application-javascript // node app.js -// # this may be run again again + +// NOTE: If you see kind an error like these: +/* + 2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied + ******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied + + OR + + Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]] + ******** FAILED to run the application: Error: Identity not found in wallet: appUser +*/ +// Delete the /fabric-samples/asset-transfer-basic/application-javascript/wallet directory +// and retry this application. +// +// The certificate authority must have been restarted and the saved certificates for the +// admin and application user are not valid. Deleting the wallet store will force these to be reset +// with the new certificate authority. +// /** - * A test application to show basic operations with any of the asset-transfer-basic chaincodes + * A test application to show basic queries operations with any of the asset-transfer-basic chaincodes * -- How to submit a transaction * -- How to query and check the results * @@ -64,7 +85,7 @@ async function main() { // in a real application this would be done only when a new user was required to be added // and would be part of an administrative flow - await registerUser(caClient, wallet, userId, 'org1.department1'); + await registerAndEnrollUser(caClient, wallet, userId, 'org1.department1'); // Create a new gateway instance for interacting with the fabric network. // In a real application this would be done as the backend server session is setup for diff --git a/asset-transfer-basic/application-javascript/package.json b/asset-transfer-basic/application-javascript/package.json index b8d3ca6e..5a149bd8 100644 --- a/asset-transfer-basic/application-javascript/package.json +++ b/asset-transfer-basic/application-javascript/package.json @@ -10,7 +10,7 @@ "author": "Hyperledger", "license": "Apache-2.0", "dependencies": { - "fabric-ca-client": "2.2.0", - "fabric-network": "2.2.0" + "fabric-ca-client": "^2.2.0", + "fabric-network": "^2.2.0" } } diff --git a/asset-transfer-ledger-queries/application-javascript/.eslintignore b/asset-transfer-ledger-queries/application-javascript/.eslintignore new file mode 100644 index 00000000..15958470 --- /dev/null +++ b/asset-transfer-ledger-queries/application-javascript/.eslintignore @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +coverage diff --git a/asset-transfer-ledger-queries/application-javascript/.eslintrc.js b/asset-transfer-ledger-queries/application-javascript/.eslintrc.js new file mode 100644 index 00000000..6fa636ba --- /dev/null +++ b/asset-transfer-ledger-queries/application-javascript/.eslintrc.js @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +module.exports = { + env: { + node: true, + mocha: true + }, + parserOptions: { + ecmaVersion: 8, + sourceType: 'script' + }, + extends: "eslint:recommended", + rules: { + indent: ['error', 'tab'], + 'linebreak-style': ['error', 'unix'], + quotes: ['error', 'single'], + semi: ['error', 'always'], + 'no-unused-vars': ['error', { args: 'none' }], + 'no-console': 'off', + curly: 'error', + eqeqeq: 'error', + 'no-throw-literal': 'error', + strict: 'error', + 'no-var': 'error', + 'dot-notation': 'error', + 'no-trailing-spaces': 'error', + 'no-use-before-define': 'error', + 'no-useless-call': 'error', + 'no-with': 'error', + 'operator-linebreak': 'error', + yoda: 'error', + 'quote-props': ['error', 'as-needed'] + } +}; diff --git a/asset-transfer-ledger-queries/application-javascript/.gitignore b/asset-transfer-ledger-queries/application-javascript/.gitignore new file mode 100644 index 00000000..21b287f7 --- /dev/null +++ b/asset-transfer-ledger-queries/application-javascript/.gitignore @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +wallet +!wallet/.gitkeep diff --git a/asset-transfer-ledger-queries/application-javascript/app.js b/asset-transfer-ledger-queries/application-javascript/app.js new file mode 100644 index 00000000..ef9bb653 --- /dev/null +++ b/asset-transfer-ledger-queries/application-javascript/app.js @@ -0,0 +1,242 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const {Gateway, Wallets} = require('fabric-network'); +const FabricCAServices = require('fabric-ca-client'); +const path = require('path'); +const {buildCAClient, registerAndEnrollUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js'); +const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js'); + +const channelName = 'mychannel'; +const chaincodeName = 'ledger'; +const walletPath = path.join(__dirname, 'wallet'); +const userId = 'appUser'; + +function prettyJSONString(inputString) { + return JSON.stringify(JSON.parse(inputString), null, 2); +} + +// pre-requisites: +// - fabric-sample two organization test-network setup with two peers, ordering service, +// and 2 certificate authorities, with the state database using couchdb +// ===> from directory /fabric-samples/test-network +// ./network.sh up createChannel -ca -s couchdb +// - Use any of the asset-transfer-ledger-queries chaincodes deployed on the channel "mychannel" +// with the chaincode name of "ledger". The following deploy command will package, +// install, approve, and commit the javascript chaincode, all the actions it takes +// to deploy a chaincode to a channel. +// ===> from directory /fabric-samples/test-network +// ./network.sh deployCC -ccn ledger -ccl javascript +// - Be sure that node.js is installed +// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript +// node -v +// - npm installed code dependencies +// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript +// npm install +// - to run this test application +// ===> from directory /fabric-samples/asset-transfer-ledger-queries/application-javascript +// node app.js + +// NOTE: If you see kind an error like these: +/* + 2020-08-07T20:23:17.590Z - error: [DiscoveryService]: send[mychannel] - Channel:mychannel received discovery error:access denied + ******** FAILED to run the application: Error: DiscoveryService: mychannel error: access denied + + OR + + Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]] + ******** FAILED to run the application: Error: Identity not found in wallet: appUser +*/ +// Delete the /fabric-samples/asset-transfer-ledger-queries/application-javascript/wallet directory +// and retry this application. +// +// The certificate authority must have been restarted and the saved certificates for the +// admin and application user are not valid. Deleting the wallet store will force these to be reset +// with the new certificate authority. +// + +/** + * A test application to show ledger queries operations with any of the asset-transfer-ledger-queries chaincodes + * -- How to submit a transaction + * -- How to query and check the results + * + * To see the SDK workings, try setting the logging to show on the console before running + * export HFC_LOGGING='{"debug":"console"}' + */ +async function main() { + let skipInit = false; + if (process.argv.length > 2) { + if (process.argv[2] === 'skipInit') { + skipInit = true; + } + } + + try { + // build an in memory object with the network configuration (also known as a connection profile) + const ccp = buildCCP(); + + // build an instance of the fabric ca services client based on + // the information in the network configuration + const caClient = buildCAClient(FabricCAServices, ccp); + + // setup the wallet to hold the credentials of the application user + const wallet = await buildWallet(Wallets, walletPath); + + // in a real application this would be done on an administrative flow, and only once + await enrollAdmin(caClient, wallet); + + // in a real application this would be done only when a new user was required to be added + // and would be part of an administrative flow + await registerAndEnrollUser(caClient, wallet, userId, 'org1.department1'); + + // Create a new gateway instance for interacting with the fabric network. + // In a real application this would be done as the backend server session is setup for + // a user that has been verified. + const gateway = new Gateway(); + + try { + // setup the gateway instance + // The user will now be able to create connections to the fabric network and be able to + // submit transactions and query. All transactions submitted by this gateway will be + // signed by this user using the credentials stored in the wallet. + await gateway.connect(ccp, { + wallet, + identity: userId, + discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally + }); + + // Build a network instance based on the channel where the smart contract is deployed + const network = await gateway.getNetwork(channelName); + + // Get the contract from the network. + const contract = network.getContract(chaincodeName); + + // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function. + // This type of transaction would only be run once by an application the first time it was started after it + // deployed the first time. Any updates to the chaincode deployed later would likely not need to run + // an "init" type function. + if (!skipInit) { + try { + console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger'); + await contract.submitTransaction('InitLedger'); + console.log('*** Result: committed'); + } catch(initError) { + // this is error is OK if we are rerunning this app without restarting + console.log(`******** initLedger failed :: ${initError}`) + } + } else { + console.log('*** not executing "InitLedger'); + } + + let result; + + // Let's try a query operation (function). + // This will be sent to just one peer and the results will be shown. + console.log('\n--> Evaluate Transaction: GetAssetsByRange, function returns assets in a specific range from asset1 to before asset6'); + result = await contract.evaluateTransaction('GetAssetsByRange', 'asset1', 'asset6'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and open end range to return assest1 to asset6'); + result = await contract.evaluateTransaction('GetAssetsByRange', '', ''); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an fixed start (asset3) and open end range to return assest3 to asset6'); + result = await contract.evaluateTransaction('GetAssetsByRange', 'asset3', ''); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Evaluate Transaction: GetAssetsByRange, function use an open start and fixed end (asset3) range to return assest1 to asset2'); + result = await contract.evaluateTransaction('GetAssetsByRange', '', 'asset3'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Now let's try to submit a transaction. + // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent + // to the orderer to be committed by each of the peer's to the channel ledger. + console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID(asset7), color(yellow), size(5), owner(Tom), and appraisedValue(1300) arguments'); + await contract.submitTransaction('CreateAsset', 'asset7', 'yellow', '5', 'Tom', '1300'); + console.log('*** Result: committed'); + + console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset7)'); + result = await contract.evaluateTransaction('ReadAsset', 'asset7'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with ID(asset7) exist'); + result = await contract.evaluateTransaction('AssetExists', 'asset7'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Now let's try to submit a transaction that deletes an asset + // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent + // to the orderer to be committed by each of the peer's to the channel ledger. + console.log('\n--> Submit Transaction: DeleteAsset with ID(asset7)'); + await contract.submitTransaction('DeleteAsset', 'asset7'); + console.log('*** Result: committed'); + + console.log('\n--> Evaluate Transaction: AssetExists, function returns "false" if an asset with ID(asset7) does not exist'); + result = await contract.evaluateTransaction('AssetExists', 'asset7'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`) + + console.log('\n--> Submit Transaction: TransferAsset, transfer asset(asset2) to new owner(Tom)'); + await contract.submitTransaction('TransferAsset', 'asset2', 'Tom'); + console.log('*** Result: committed'); + + console.log('\n--> Evaluate Transaction: ReadAsset, function returns information about an asset with ID(asset2)'); + result = await contract.evaluateTransaction('ReadAsset', 'asset2'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Rich Query with Pagination (Only supported if CouchDB is used as state database) + console.log('\n--> Evaluate Transaction: QueryAssetsWithPagination, function returns "Tom" assets'); + result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}','3',''); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Submit Transaction: TransferAssetByColor, transfer all yellow assets to new owner(Michel)'); + await contract.submitTransaction('TransferAssetByColor', 'yellow', 'Michel'); + console.log('*** Result: committed'); + + // Rich Query (Only supported if CouchDB is used as state database): + console.log('\n--> Evaluate Transaction: QueryAssetsByOwner, find all assets with owner(Michel)'); + result = await contract.evaluateTransaction('QueryAssetsByOwner', 'Michel'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('\n--> Evaluate Transaction: GetAssetHistory, get the history of an asset(asset7)'); + result = await contract.evaluateTransaction('GetAssetHistory', 'asset7'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Rich Query (Only supported if CouchDB is used as state database): + console.log('\n--> Evaluate Transaction: QueryAssets, assets of size 15'); + result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"size":15}}'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database): + console.log('\n--> Evaluate Transaction: QueryAssets, Jin Soo\'s assets'); + result = await contract.evaluateTransaction('QueryAssets', '{"selector":{"docType":"asset","owner":"Jin Soo"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Rich Query with Pagination (Only supported if CouchDB is used as state database) + console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 1 of assets from asset3 to asset6 (asset3, asset4)'); + result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', ''); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + // Rich Query with Pagination (Only supported if CouchDB is used as state database) + console.log('\n--> Evaluate Transaction: GetAssetsByRangeWithPagination - get page 2 of assets from asset3 to asset6 (asset4, asset5)'); + result = await contract.evaluateTransaction('GetAssetsByRangeWithPagination', 'asset3', 'asset6', '2', 'asset4'); + console.log(`*** Result: ${prettyJSONString(result.toString())}`); + + console.log('*** all tests completed'); + } finally { + // Disconnect from the gateway when the application is closing + // This will close all connections to the network + gateway.disconnect(); + } + } catch (error) { + console.error(`******** FAILED to run the application: ${error}`); + } + + console.log('*** application ending'); + +} + +main(); diff --git a/asset-transfer-ledger-queries/application-javascript/package.json b/asset-transfer-ledger-queries/application-javascript/package.json new file mode 100644 index 00000000..5a149bd8 --- /dev/null +++ b/asset-transfer-ledger-queries/application-javascript/package.json @@ -0,0 +1,16 @@ +{ + "name": "asset-transfer-basic", + "version": "1.0.0", + "description": "Asset-transfer-basic application implemented in JavaScript", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.2.0", + "fabric-network": "^2.2.0" + } +} diff --git a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go index 5efbe3d7..44cbdb1f 100644 --- a/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go +++ b/asset-transfer-ledger-queries/chaincode-go/asset_transfer_ledger_chaincode.go @@ -99,7 +99,7 @@ type Asset struct { // HistoryQueryResult structure used for returning result of history query type HistoryQueryResult struct { Record *Asset `json:"record"` - TxID string `json:"txID"` + TxId string `json:"txId"` Timestamp time.Time `json:"timestamp"` IsDelete bool `json:"isDelete"` } @@ -379,6 +379,8 @@ func getQueryResultForQueryStringWithPagination(ctx contractapi.TransactionConte // GetAssetHistory returns the chain of custody for an asset since issuance. func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInterface, assetID string) ([]HistoryQueryResult, error) { + log.Printf("GetAssetHistory: ID %v", assetID) + resultsIterator, err := ctx.GetStub().GetHistoryForKey(assetID) if err != nil { return nil, err @@ -393,17 +395,24 @@ func (t *SimpleChaincode) GetAssetHistory(ctx contractapi.TransactionContextInte } var asset Asset - err = json.Unmarshal(response.Value, &asset) - if err != nil { - return nil, err + if len(response.Value) > 0 { + err = json.Unmarshal(response.Value, &asset) + if err != nil { + return nil, err + } + } else { + asset = Asset{ + ID: assetID, + } } timestamp, err := ptypes.Timestamp(response.Timestamp) if err != nil { return nil, err } + record := HistoryQueryResult{ - TxID: response.TxId, + TxId: response.TxId, Timestamp: timestamp, Record: &asset, IsDelete: response.IsDelete, diff --git a/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js index 44ba1038..c44990b2 100644 --- a/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js +++ b/asset-transfer-ledger-queries/chaincode-javascript/lib/asset_transfer_ledger_chaincode.js @@ -81,7 +81,7 @@ class Chaincode extends Contract{ // ==== Create asset object and marshal to JSON ==== let asset = {}; asset.docType = 'asset'; - asset.assetID = assetID; + asset.ID = assetID; asset.color = color; asset.size = size; asset.owner = owner; @@ -90,7 +90,7 @@ class Chaincode extends Contract{ // === Save asset to state === await ctx.stub.putState(assetID, Buffer.from(JSON.stringify(asset))); let indexName = 'color~name' - let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.assetID]); + let colorNameIndexKey = await ctx.stub.createCompositeKey(indexName, [asset.color, asset.ID]); // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble. // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value @@ -137,7 +137,7 @@ class Chaincode extends Contract{ // delete the index let indexName = 'color~name'; - let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.assetID]); + let colorNameIndexKey = ctx.stub.createCompositeKey(indexName, [assetJSON.color, assetJSON.ID]); if (!colorNameIndexKey) { throw new Error(' Failed to create the createCompositeKey'); } @@ -145,7 +145,7 @@ class Chaincode extends Contract{ await ctx.stub.deleteState(colorNameIndexKey); } - // TransferAsset transfers a asset by setting a new owner name on the asset + // TransferAsset transfers an asset by setting a new owner name on the asset async TransferAsset(ctx, assetName, newOwner) { let assetAsBytes = await ctx.stub.getState(assetName); @@ -245,7 +245,7 @@ class Chaincode extends Contract{ // GetQueryResultForQueryString executes the passed in query string. // Result set is built and returned as a byte array containing the JSON results. async GetQueryResultForQueryString(ctx, queryString) { - + let resultsIterator = await ctx.stub.getQueryResult(queryString); let results = await this.GetAllResults(resultsIterator, false); @@ -258,7 +258,7 @@ class Chaincode extends Contract{ // The number of fetched records will be equal to or lesser than the page size. // Paginated range queries are only valid for read only transactions. async GetAssetsByRangeWithPagination(ctx, startKey, endKey, pageSize, bookmark) { - + const { iterator, metadata } = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark); const results = await this.GetAllResults(iterator, false); @@ -293,8 +293,8 @@ class Chaincode extends Contract{ // GetAssetHistory returns the chain of custody for an asset since issuance. async GetAssetHistory(ctx, assetName) { - let resultsIterator = await ctx.stub.getHistoryForKey(assetName); - let results = await this.GetAllResults(resultsIterator, true); + const resultsIterator = await ctx.stub.getHistoryForKey(assetName); + const results = await this.GetAllResults(resultsIterator, true); return JSON.stringify(results); } @@ -302,7 +302,7 @@ class Chaincode extends Contract{ // AssetExists returns true when asset with given ID exists in world state async AssetExists(ctx, assetName) { // ==== Check if asset already exists ==== - let assetState = await ctx.stub.getState(assetName); + const assetState = await ctx.stub.getState(assetName); if ( !assetState || assetState.length === 0 ) { return false; } @@ -310,80 +310,74 @@ class Chaincode extends Contract{ } async GetAllResults(iterator, isHistory) { - let allResults = []; - while (true) { - let res = await iterator.next(); + const allResults = []; + let res = await iterator.next(); + while (!res.done) { + const jsonRes = {}; - if (res.value && res.value.value.toString()) { - let jsonRes = {}; - console.log(res.value.value.toString('utf8')); - if (isHistory && isHistory === true) { - jsonRes.TxId = res.value.tx_id; - jsonRes.Timestamp = res.value.timestamp; - try { - jsonRes.Value = JSON.parse(res.value.value.toString('utf8')); - } catch (err) { - console.log(err); - jsonRes.Value = res.value.value.toString('utf8'); - } - } else { - jsonRes.Key = res.value.key; - try { - jsonRes.Record = JSON.parse(res.value.value.toString('utf8')); - } catch (err) { - console.log(err); - jsonRes.Record = res.value.value.toString('utf8'); - } - } - allResults.push(jsonRes); + if (isHistory) { + jsonRes.TxId = res.value.txId; + jsonRes.Timestamp = res.value.timestamp; + jsonRes.IsDelete = res.value.isDelete; } - if (res.done) { - await iterator.close(); - return allResults; + + if (res.value.value.length > 0) { + jsonRes.Record = JSON.parse(res.value.value.toString('utf8')); + } else { + jsonRes.Record = {ID: res.value.key}; } + + jsonRes.Key = res.value.key; + + console.log('Result: ' + JSON.stringify(jsonRes)); + allResults.push(jsonRes); + res = await iterator.next(); } + + await iterator.close(); + return allResults; } // InitLedger creates sample assets in the ledger async InitLedger(ctx) { const assets = [ { - assetID: 'asset1', + ID: 'asset1', color: 'blue', size: 5, owner: 'Tom', appraisedValue: 100 }, { - assetID: 'asset2', + ID: 'asset2', color: 'red', size: 5, owner: 'Brad', appraisedValue: 100 }, { - assetID: 'asset3', + ID: 'asset3', color: 'green', size: 10, owner: 'Jin Soo', appraisedValue: 200 }, { - assetID: 'asset4', + ID: 'asset4', color: 'yellow', size: 10, owner: 'Max', appraisedValue: 200 }, { - assetID: 'asset5', + ID: 'asset5', color: 'black', size: 15, owner: 'Adriana', appraisedValue: 250 }, { - assetID: 'asset6', + ID: 'asset6', color: 'white', size: 15, owner: 'Michel', @@ -394,7 +388,7 @@ class Chaincode extends Contract{ for (let i = 0; i < assets.length; i++) { await this.CreateAsset( ctx, - assets[i].assetID, + assets[i].ID, assets[i].color, assets[i].size, assets[i].owner, diff --git a/ci/scripts/run-test-network-basic.sh b/ci/scripts/run-test-network-basic.sh index 9d0fe2b9..903e634a 100755 --- a/ci/scripts/run-test-network-basic.sh +++ b/ci/scripts/run-test-network-basic.sh @@ -27,3 +27,4 @@ popd print "Stopping network" ./network.sh down +rm -R ../asset-transfer-basic/application-javascript/wallet diff --git a/ci/scripts/run-test-network-ledger.sh b/ci/scripts/run-test-network-ledger.sh index 60b00612..36b96b91 100755 --- a/ci/scripts/run-test-network-ledger.sh +++ b/ci/scripts/run-test-network-ledger.sh @@ -1,7 +1,6 @@ set -euo pipefail FABRIC_VERSION=${FABRIC_VERSION:-2.2} -CHAINCODE_LANGUAGE=${CHAINCODE_LANGUAGE:-go} CHAINCODE_NAME=${CHAINCODE_NAME:-ledger} function print() { @@ -14,8 +13,28 @@ function print() { print "Creating network" ./network.sh up createChannel -ca -s couchdb -i "${FABRIC_VERSION}" -print "Deploying ${CHAINCODE_NAME} chaincode" -./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1 -ccs 1 -ccl "${CHAINCODE_LANGUAGE}" +print "Deploying ${CHAINCODE_NAME} go chaincode" +./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 1.0 -ccs 1 -ccl go + +# Run Javascript application against the go chaincode +print "Initializing Javascript application" +pushd ../asset-transfer-ledger-queries/application-javascript +npm install +print "Executing app.js" +node app.js +popd + +print "Deploying ${CHAINCODE_NAME} javascript chaincode" +./network.sh deployCC -ccn "${CHAINCODE_NAME}" -ccv 2.0 -ccs 2 -ccl javascript + +# Run Javascript application against the javascript chaincode +print "Initializing Javascript application" +pushd ../asset-transfer-ledger-queries/application-javascript +npm install +print "Executing app.js" +node app.js skipInit +popd print "Stopping network" ./network.sh down +rm -R ../asset-transfer-ledger-queries/application-javascript/wallet \ No newline at end of file diff --git a/test-application/javascript/CAUtil.js b/test-application/javascript/CAUtil.js index 6d2d1fb7..1d192352 100644 --- a/test-application/javascript/CAUtil.js +++ b/test-application/javascript/CAUtil.js @@ -6,9 +6,6 @@ 'use strict'; -const fs = require('fs'); -const path = require('path'); - const adminUserId = 'admin'; const adminUserPasswd = 'adminpw'; @@ -53,7 +50,7 @@ exports.enrollAdmin = async (caClient, wallet) => { } }; -exports.registerUser = async (caClient, wallet, userId, affiliation) => { +exports.registerAndEnrollUser = async (caClient, wallet, userId, affiliation) => { try { // Check to see if we've already enrolled the user const userIdentity = await wallet.get(userId);