From c18419644d072056a32c64cea2938a032a75f429 Mon Sep 17 00:00:00 2001 From: Simon Stone Date: Tue, 20 Nov 2018 16:01:36 +0000 Subject: [PATCH] [FAB-12878] Add fabcar app using new prog model (TS) Add a new version of the FabCar applications using the new programming model (fabric-network), written in TypeScript. The new programming model requires a connection profile, so I have added connection profiles (JSON and YAML) versions to the basic-network sample. When we switch FabCar to use first-network, we will have to add connection profiles into that as well. Change-Id: I81bae907fc64a1cde0234325f37b53e5cd7964e5 Signed-off-by: Simon Stone --- basic-network/connection.json | 52 +++++++++++++++++ basic-network/connection.yaml | 33 +++++++++++ fabcar/typescript/.editorconfig | 16 ++++++ fabcar/typescript/.gitignore | 83 +++++++++++++++++++++++++++ fabcar/typescript/package.json | 60 +++++++++++++++++++ fabcar/typescript/src/enrollAdmin.ts | 45 +++++++++++++++ fabcar/typescript/src/invoke.ts | 54 +++++++++++++++++ fabcar/typescript/src/query.ts | 51 ++++++++++++++++ fabcar/typescript/src/registerUser.ts | 57 ++++++++++++++++++ fabcar/typescript/tsconfig.json | 16 ++++++ fabcar/typescript/tslint.json | 22 +++++++ fabcar/typescript/wallet/.gitkeep | 0 scripts/Jenkins_Scripts/fabcar.sh | 23 +++++++- 13 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 basic-network/connection.json create mode 100644 basic-network/connection.yaml create mode 100755 fabcar/typescript/.editorconfig create mode 100644 fabcar/typescript/.gitignore create mode 100644 fabcar/typescript/package.json create mode 100644 fabcar/typescript/src/enrollAdmin.ts create mode 100644 fabcar/typescript/src/invoke.ts create mode 100644 fabcar/typescript/src/query.ts create mode 100644 fabcar/typescript/src/registerUser.ts create mode 100644 fabcar/typescript/tsconfig.json create mode 100644 fabcar/typescript/tslint.json create mode 100644 fabcar/typescript/wallet/.gitkeep diff --git a/basic-network/connection.json b/basic-network/connection.json new file mode 100644 index 0000000000..54cfca3276 --- /dev/null +++ b/basic-network/connection.json @@ -0,0 +1,52 @@ +{ + "name": "basic-network", + "version": "1.0.0", + "client": { + "organization": "Org1", + "connection": { + "timeout": { + "peer": { + "endorser": "300" + }, + "orderer": "300" + } + } + }, + "channels": { + "mychannel": { + "orderers": [ + "orderer.example.com" + ], + "peers": { + "peer0.org1.example.com": {} + } + } + }, + "organizations": { + "Org1": { + "mspid": "Org1MSP", + "peers": [ + "peer0.org1.example.com" + ], + "certificateAuthorities": [ + "ca.example.com" + ] + } + }, + "orderers": { + "orderer.example.com": { + "url": "grpc://localhost:7050" + } + }, + "peers": { + "peer0.org1.example.com": { + "url": "grpc://localhost:7051" + } + }, + "certificateAuthorities": { + "ca.example.com": { + "url": "http://localhost:7054", + "caName": "ca.example.com" + } + } +} diff --git a/basic-network/connection.yaml b/basic-network/connection.yaml new file mode 100644 index 0000000000..2493b1219a --- /dev/null +++ b/basic-network/connection.yaml @@ -0,0 +1,33 @@ +--- +name: basic-network +version: 1.0.0 +client: + organization: Org1 + connection: + timeout: + peer: + endorser: '300' + orderer: '300' +channels: + mychannel: + orderers: + - orderer.example.com + peers: + peer0.org1.example.com: {} +organizations: + Org1: + mspid: Org1MSP + peers: + - peer0.org1.example.com + certificateAuthorities: + - ca.example.com +orderers: + orderer.example.com: + url: grpc://localhost:7050 +peers: + peer0.org1.example.com: + url: grpc://localhost:7051 +certificateAuthorities: + ca.example.com: + url: http://localhost:7054 + caName: ca.example.com diff --git a/fabcar/typescript/.editorconfig b/fabcar/typescript/.editorconfig new file mode 100755 index 0000000000..75a13be205 --- /dev/null +++ b/fabcar/typescript/.editorconfig @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/fabcar/typescript/.gitignore b/fabcar/typescript/.gitignore new file mode 100644 index 0000000000..2a6c904c7d --- /dev/null +++ b/fabcar/typescript/.gitignore @@ -0,0 +1,83 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Compiled TypeScript files +dist + +wallet +!wallet/.gitkeep diff --git a/fabcar/typescript/package.json b/fabcar/typescript/package.json new file mode 100644 index 0000000000..2b0326c9b7 --- /dev/null +++ b/fabcar/typescript/package.json @@ -0,0 +1,60 @@ +{ + "name": "fabcar", + "version": "1.0.0", + "description": "FabCar application implemented in TypeScript", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "unstable", + "fabric-client": "unstable", + "fabric-network": "unstable" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^13.1.0", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "typescript": "^3.1.6" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/fabcar/typescript/src/enrollAdmin.ts b/fabcar/typescript/src/enrollAdmin.ts new file mode 100644 index 0000000000..226f76b780 --- /dev/null +++ b/fabcar/typescript/src/enrollAdmin.ts @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as FabricCAServices from 'fabric-ca-client'; +import { FileSystemWallet, X509WalletMixin } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +const ccpPath = path.resolve(__dirname, '..', '..', '..', 'basic-network', 'connection.json'); +const ccpJSON = fs.readFileSync(ccpPath, 'utf8'); +const ccp = JSON.parse(ccpJSON); + +async function main() { + try { + + // Create a new CA client for interacting with the CA. + const caURL = ccp.certificateAuthorities['ca.example.com'].url; + const ca = new FabricCAServices(caURL); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = new FileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the admin user. + const adminExists = await wallet.exists('admin'); + if (adminExists) { + console.log('An identity for the admin user "admin" already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const identity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes()); + wallet.import('admin', identity); + console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user "admin": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/invoke.ts b/fabcar/typescript/src/invoke.ts new file mode 100644 index 0000000000..b4120df88a --- /dev/null +++ b/fabcar/typescript/src/invoke.ts @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FileSystemWallet, Gateway } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +const ccpPath = path.resolve(__dirname, '..', '..', '..', 'basic-network', 'connection.json'); +const ccpJSON = fs.readFileSync(ccpPath, 'utf8'); +const ccp = JSON.parse(ccpJSON); + +async function main() { + try { + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = new FileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userExists = await wallet.exists('user1'); + if (!userExists) { + console.log('An identity for the user "user1" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await (gateway as any).connect(ccp, { wallet, identity: 'user1', discovery: { useLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Submit the specified transaction. + // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') + // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave') + await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom'); + console.log(`Transaction has been submitted`); + + // Disconnect from the gateway. + await gateway.disconnect(); + + } catch (error) { + console.error(`Failed to submit transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/query.ts b/fabcar/typescript/src/query.ts new file mode 100644 index 0000000000..7598077dfb --- /dev/null +++ b/fabcar/typescript/src/query.ts @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FileSystemWallet, Gateway } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +const ccpPath = path.resolve(__dirname, '..', '..', '..', 'basic-network', 'connection.json'); +const ccpJSON = fs.readFileSync(ccpPath, 'utf8'); +const ccp = JSON.parse(ccpJSON); + +async function main() { + try { + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = new FileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userExists = await wallet.exists('user1'); + if (!userExists) { + console.log('An identity for the user "user1" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'user1' }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Evaluate the specified transaction. + // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') + // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') + const result = await contract.evaluateTransaction('queryAllCars'); + console.log(`Transaction has been evaluated, result is: ${result.toString()}`); + + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/registerUser.ts b/fabcar/typescript/src/registerUser.ts new file mode 100644 index 0000000000..3d253cf1ee --- /dev/null +++ b/fabcar/typescript/src/registerUser.ts @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FileSystemWallet, Gateway, X509WalletMixin } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +const ccpPath = path.resolve(__dirname, '..', '..', '..', 'basic-network', 'connection.json'); +const ccpJSON = fs.readFileSync(ccpPath, 'utf8'); +const ccp = JSON.parse(ccpJSON); + +async function main() { + try { + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = new FileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userExists = await wallet.exists('user1'); + if (userExists) { + console.log('An identity for the user "user1" already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminExists = await wallet.exists('admin'); + if (!adminExists) { + console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('Run the enrollAdmin.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'admin' }); + + // Get the CA client object from the gateway for interacting with the CA. + const ca = gateway.getClient().getCertificateAuthority(); + const adminIdentity = gateway.getCurrentIdentity(); + + // Register the user, enroll the user, and import the new identity into the wallet. + const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'user1', role: 'client' }, adminIdentity); + const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: secret }); + const userIdentity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes()); + wallet.import('user1', userIdentity); + console.log('Successfully registered and enrolled admin user "user1" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user "user1": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/tsconfig.json b/fabcar/typescript/tsconfig.json new file mode 100644 index 0000000000..8c96ea0719 --- /dev/null +++ b/fabcar/typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/fabcar/typescript/tslint.json b/fabcar/typescript/tslint.json new file mode 100644 index 0000000000..e08fd0b868 --- /dev/null +++ b/fabcar/typescript/tslint.json @@ -0,0 +1,22 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"], + "max-line-length": false + }, + "rulesDirectory": [] +} diff --git a/fabcar/typescript/wallet/.gitkeep b/fabcar/typescript/wallet/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Jenkins_Scripts/fabcar.sh b/scripts/Jenkins_Scripts/fabcar.sh index 4f2355e8de..3dce2da1c0 100755 --- a/scripts/Jenkins_Scripts/fabcar.sh +++ b/scripts/Jenkins_Scripts/fabcar.sh @@ -34,9 +34,30 @@ export PATH=gopath/src/github.com/hyperledger/fabric-samples/bin:$PATH LANGUAGES="go javascript typescript" for LANGUAGE in ${LANGUAGES}; do echo -e "\033[32m starting fabcar test (${LANGUAGE})" "\033[0m" + # Start Fabric, and deploy the smart contract ./startFabric.sh ${LANGUAGE} copy_logs $? fabcar-${LANGUAGE} - docker ps -aq | xargs docker rm -f + # If an application exists for this language, test it + if [ -d ${LANGUAGE} ]; then + pushd ${LANGUAGE} + if [ ${LANGUAGE} = "typescript" ]; then + COMMAND=node + PREFIX=dist/ + SUFFIX=.js + npm install + npm run build + fi + ${COMMAND} ${PREFIX}enrollAdmin${SUFFIX} + copy_logs $? fabcar-${LANGUAGE}-enrollAdmin + ${COMMAND} ${PREFIX}registerUser${SUFFIX} + copy_logs $? fabcar-${LANGUAGE}-registerUser + ${COMMAND} ${PREFIX}query${SUFFIX} + copy_logs $? fabcar-${LANGUAGE}-query + ${COMMAND} ${PREFIX}invoke${SUFFIX} + copy_logs $? fabcar-${LANGUAGE}-invoke + popd + fi + docker ps -aq | xargs docker rm -f docker rmi -f $(docker images -aq dev-*) echo -e "\033[32m finished fabcar test (${LANGUAGE})" "\033[0m" done