diff --git a/README.md b/README.md index 75f41b2..6d48001 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ _A CLI tools built for AElf_ ## Features -- Get or Set common configs, `endpoint`, `account`, `datadir`, `password`, `csv`. +- Get or Set common configs, `endpoint`, `account`, `datadir`, `password`, `csv`, `json`. - For new users who are not familiar with the CLI parameters, any missing parameters will be asked in a prompting way. - Create a new `account`. - Load an account from a given `private key` or `mnemonic`. @@ -130,7 +130,7 @@ Welcome to aelf interactive console. Ctrl + C to terminate the program. Double t ║ NAME | DESCRIPTION ║ ║ AElf | imported from aelf-sdk ║ ║ aelf | the instance of an aelf-sdk, connect to ║ - ║ | http://13.231.179.27:8000 ║ + ║ | https://tdvw-test-node.aelf.io ║ ║ _account | the instance of an AElf wallet, address ║ ║ | is ║ ║ | 2Ue31YTuB5Szy7cnr3SCEGU2gtGi5uMQBYarYUR… ║ @@ -155,6 +155,7 @@ Options: -p, --password The password of encrypted keyStore -d, --datadir The directory that contains the AElf related files. Defaults to {home}/.local/share/aelf -c, --csv The location of the CSV file containing the parameters. + -j, --json The location of the JSON file containing the parameters. -h, --help output usage information Commands: @@ -216,7 +217,8 @@ aelf-command console - `endpoint`: The endpoint for the RPC service. - `account`: The account to be used to interact with the blockchain `endpoint`. - `password`: The password for unlocking the given `account`. -- `csv>`: The location of the CSV file containing the parameters. +- `csv`: The location of the CSV file containing the parameters. +- `json`: The location of the JSON file containing the parameters. You can specified options above in several ways, and the priority is in the order of low to high. @@ -670,7 +672,7 @@ In each item: ```bash $ aelf-command send -✔ Enter the the URI of an AElf node … http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node … https://tdvw-test-node.aelf.io ✔ Enter a valid wallet address, if you don't have, create one by aelf-command create … D3vSjRYL8MpeRpvUDy85ktXijnBe2tHn8NTACsggUVteQCNGP ✔ Enter the password you typed when creating a wallet … ******** ✔ Enter contract name (System contracts only) or the address of contract … AElf.ContractNames.Token @@ -708,7 +710,7 @@ aelf-command send AElf.ContractNames.Token Transfer '{"symbol": "ELF", "to": "C9 ```bash $ aelf-command call -✔ Enter the the URI of an AElf node … http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node … https://tdvw-test-node.aelf.io ✔ Enter a valid wallet address, if you don't have, create one by aelf-command create … D3vSjRYL8MpeRpvUDy85ktXijnBe2tHn8NTACsggUVteQCNGP ✔ Enter the password you typed when creating a wallet … ******** ✔ Enter contract name (System contracts only) or the address of contract … AElf.ContractNames.Token @@ -744,6 +746,38 @@ Result: aelf-command call AElf.ContractNames.Token GetTokenInfo '{"symbol":"ELF"}' ``` +#### Call a read-only method and pass params through CSV + +```bash +$ aelf-command call -e https://tdvw-test-node.aelf.io/ -a GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk -p 1234*Qwer -j ./test.csv AElf.ContractNames.Token GetBalance +✔ Fetching contract successfully! +✔ Calling method successfully! +AElf [Info]: +Result: +{ + "symbol": "ELF", + "owner": "GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk", + "balance": "155000" +} +✔ Succeed! +``` + +#### Call a read-only method and pass params through JSON + +```bash +$ aelf-command call -e https://tdvw-test-node.aelf.io/ -a GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk -p 1234*Qwer -j ./test.json AElf.ContractNames.Token GetBalance +✔ Fetching contract successfully! +✔ Calling method successfully! +AElf [Info]: +Result: +{ + "symbol": "ELF", + "owner": "GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk", + "balance": "155000" +} +✔ Succeed! +``` + ### get-chain-status - Get the current status of the block chain ```bash @@ -770,7 +804,7 @@ $ aelf-command get-chain-status ```bash $ aelf-command get-tx-result -✔ Enter the the URI of an AElf node … http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node … https://tdvw-test-node.aelf.io ✔ Enter a valid transaction id in hex format … 7b620a49ee9666c0c381fdb33f94bd31e1b5eb0fdffa081463c3954e9f734a02 ✔ Succeed! { TransactionId: @@ -800,7 +834,7 @@ $ aelf-command get-tx-result ```bash $ aelf-command get-blk-height -✔ Enter the the URI of an AElf node … http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node … https://tdvw-test-node.aelf.io > 7902091 ``` @@ -810,7 +844,7 @@ You can pass a block height or a block hash to this sub-command. ```bash $ aelf-command get-blk-info -✔ Enter the the URI of an AElf node: http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node: https://tdvw-test-node.aelf.io ✔ Enter a valid height or block hash: 123 ✔ Include transactions whether or not: no / yes { BlockHash: @@ -845,7 +879,7 @@ aelf-command get-blk-info ca61c7c8f5fc1bc8af0536bc9b51c61a94f39641a93a748e72802b ```bash $ aelf-command console -✔ Enter the the URI of an AElf node … http://13.231.179.27:8000 +✔ Enter the the URI of an AElf node … https://tdvw-test-node.aelf.io ✔ Enter a valid wallet address, if you don't have, create one by aelf-command create … 2Ue31YTuB5Szy7cnr3SCEGU2gtGi5uMQBYarYUR5oGin1sys6H ✔ Enter the password you typed when creating a wallet … ******** ✔ Succeed! @@ -856,7 +890,7 @@ Welcome to aelf interactive console. Ctrl + C to terminate the program. Double t ║ NAME | DESCRIPTION ║ ║ AElf | imported from aelf-sdk ║ ║ aelf | instance of aelf-sdk, connect to ║ - ║ | http://13.231.179.27:8000 ║ + ║ | https://tdvw-test-node.aelf.io ║ ║ _account | instance of AElf wallet, wallet address ║ ║ | is ║ ║ | 2Ue31YTuB5Szy7cnr3SCEGU2gtGi5uMQBYarYUR… ║ diff --git a/package.json b/package.json index 048dc16..4eb6781 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aelf-command", - "version": "0.1.49-beta.0", + "version": "0.1.50-beta.0", "description": "A CLI tools for AElf", "main": "src/index.js", "type": "module", diff --git a/src/command/call.js b/src/command/call.js index 3c85282..b9ac3a8 100644 --- a/src/command/call.js +++ b/src/command/call.js @@ -1,8 +1,6 @@ import AElf from 'aelf-sdk'; import inquirer from 'inquirer'; import chalk from 'chalk'; -import { createReadStream } from 'fs'; -import csv from 'csv-parser'; import BaseSubCommand from './baseSubCommand.js'; import { callCommandUsages, callCommandParameters } from '../utils/constants.js'; import { @@ -12,7 +10,8 @@ import { promptTolerateSeveralTimes, getParams, parseJSON, - parseCSV + parseCSV, + parseJSONFile } from '../utils/utils.js'; import { getWallet } from '../utils/wallet.js'; import { logger } from '../utils/myLogger.js'; @@ -91,7 +90,7 @@ class CallCommand extends BaseSubCommand { // @ts-ignore const { options, subOptions } = await super.run(commander, ...args); const subOptionsLength = Object.keys(subOptions).length; - const { endpoint, datadir, account, password, csv } = options; + const { endpoint, datadir, account, password, csv, json } = options; const aelf = new AElf(new AElf.providers.HttpProvider(endpoint)); try { let { contractAddress, method, params } = subOptions; @@ -127,8 +126,9 @@ class CallCommand extends BaseSubCommand { contractAddress = await getContractInstance(contractAddress, aelf, wallet, this.oraInstance); method = getMethod(method, contractAddress); if (csv) { - const csvParams = await parseCSV(csv); - params = csvParams; + params = await parseCSV(csv); + } else if (json) { + params = await parseJSONFile(json); } else { params = await getParams(method); params = typeof params === 'string' ? params : BaseSubCommand.normalizeConfig(params); diff --git a/src/index.js b/src/index.js index 29b198c..00f31d6 100644 --- a/src/index.js +++ b/src/index.js @@ -56,6 +56,7 @@ function init(options) { `The directory that contains the AElf related files. Default to be ${userHomeDir}/aelf` ); commander.option('-c, --csv ', 'The location of the CSV file containing the parameters.'); + commander.option('-j, --json ', 'The location of the JSON file containing the parameters.'); const rc = new RC(); Object.values(commands).forEach(Value => { const command = new Value(rc); diff --git a/src/utils/constants.js b/src/utils/constants.js index 6a7859c..db824dc 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -371,6 +371,11 @@ const commonGlobalOptionValidatorDesc = { type: 'string', required: false, message: 'set params in csv file by -c ' + }, + json: { + type: 'string', + required: false, + message: 'set params in csv file by -j ' } }; diff --git a/src/utils/utils.js b/src/utils/utils.js index 7649f8d..a08e18f 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -481,6 +481,17 @@ const parseCSV = async address => { return results; }; +const parseJSONFile = async address => { + try { + const absolutePath = path.resolve(address); + const data = await fs.readFileSync(absolutePath); + const jsonObject = JSON.parse(data.toString('utf8')); + return jsonObject; + } catch (error) { + throw new Error(`An error occurred while reading or parsing the JSON file: ${error.message}`); + } +}; + export { promisify, camelCase, @@ -494,5 +505,6 @@ export { randomId, getParams, deserializeLogs, - parseCSV + parseCSV, + parseJSONFile }; diff --git a/test/command/call.test.js b/test/command/call.test.js index dcfc744..a5fc09c 100644 --- a/test/command/call.test.js +++ b/test/command/call.test.js @@ -7,7 +7,7 @@ import { callCommandUsages, callCommandParameters } from '../../src/utils/consta import { getContractInstance } from '../../src/utils/utils.js'; import { userHomeDir } from '../../src/utils/userHomeDir.js'; import { logger } from '../../src/utils/myLogger.js'; -import { endpoint as endPoint, account, password, dataDir, csvDir } from '../constants.js'; +import { endpoint as endPoint, account, password, dataDir, csvDir, jsonDir } from '../constants.js'; const sampleRc = { getConfigs: jest.fn() }; jest.mock('../../src/utils/myLogger'); @@ -122,6 +122,26 @@ describe('CallCommand', () => { expect(logger.info).toHaveBeenCalled(); }); + test('should run with json', async () => { + inquirer.prompt = questions => + Promise.resolve({ + symbol: 'ELF', + owner: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }); + const commander = new Command(); + commander.option('-e, --endpoint ', 'The URI of an AElf node. Eg: http://127.0.0.1:8000'); + commander.option('-a, --account ', 'The address of AElf wallet'); + commander.option('-p, --password ', 'The password of encrypted keyStore'); + commander.option( + '-d, --datadir ', + `The directory that contains the AElf related files. Default to be ${userHomeDir}/aelf` + ); + commander.option('-j, --json ', 'The location of the JSON file containing the parameters.'); + commander.parse([process.argv[0], '', 'call', '-e', endPoint, '-a', account, '-p', password, '-d', dataDir, '-j', jsonDir]); + await callCommand.run(commander, 'AElf.ContractNames.Token', 'GetBalance'); + expect(logger.info).toHaveBeenCalled(); + }); + test('should run with invalid parameters', async () => { inquirer.prompt = backup; callCommand = new CallCommand(sampleRc, 'call', 'Call a read-only method on a contract.', [ diff --git a/test/constants.js b/test/constants.js index 14f8e0e..b721778 100644 --- a/test/constants.js +++ b/test/constants.js @@ -5,3 +5,4 @@ export const account = 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk'; export const password = '1234*Qwer'; export const dataDir = path.resolve(__dirname, './dataDir/aelf'); export const csvDir = path.resolve(__dirname, './test.csv'); +export const jsonDir = path.resolve(__dirname, './test.json'); diff --git a/test/test.json b/test/test.json new file mode 100644 index 0000000..8811f51 --- /dev/null +++ b/test/test.json @@ -0,0 +1,4 @@ +{ + "symbol": "ELF", + "owner": "GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk" +} diff --git a/types/utils/constants.d.ts b/types/utils/constants.d.ts index f75d942..a1559da 100644 --- a/types/utils/constants.d.ts +++ b/types/utils/constants.d.ts @@ -11,42 +11,32 @@ export interface CallCommandParameter { } export const callCommandParameters: CallCommandParameter[]; -export interface PasswordValidatorDesc { +interface BaseValidatorDesc { type: string; required: boolean; message: string; +} +export interface PasswordValidatorDesc extends BaseValidatorDesc { validator(rule: any, value: any): boolean; } -export interface EndpointValidatorDesc { - type: string; - required: boolean; +export interface EndpointValidatorDesc extends BaseValidatorDesc { pattern: RegExp; - message: string; } -export interface DatadirValidatorDesc { - type: string; - required: boolean; - message: string; -} +export interface DatadirValidatorDesc extends BaseValidatorDesc {} -export interface AccountValidatorDesc { - type: string; - required: boolean; - message: string; -} -export interface CSVValidatorDesc { - type: string; - required: boolean; - message: string; -} +export interface AccountValidatorDesc extends BaseValidatorDesc {} +export interface CSVValidatorDesc extends BaseValidatorDesc {} + +export interface JSONValidatorDesc extends BaseValidatorDesc {} export interface CommonGlobalOptionValidatorDesc { password: PasswordValidatorDesc; endpoint: EndpointValidatorDesc; datadir: DatadirValidatorDesc; account: AccountValidatorDesc; csv: CSVValidatorDesc; + json: JSONValidatorDesc; } export const commonGlobalOptionValidatorDesc: CommonGlobalOptionValidatorDesc;