From 7b9c652f61c75f4942bbcf3c828961137b2572d2 Mon Sep 17 00:00:00 2001 From: Erin Hughes Date: Fri, 22 Nov 2019 13:58:26 +0000 Subject: [PATCH] Adding basic evaluate functionality to the transaction view Signed-off-by: Erin Hughes --- .../commands/openTransactionViewCommand.ts | 27 ++++++++++------- .../extension/util/ExtensionUtil.ts | 2 +- .../extension/webview/TransactionView.ts | 2 ++ .../openTransactionViewCommand.test.ts | 8 +++++ .../test/commands/submitTransaction.test.ts | 19 ++++++++++++ .../test/webview/TransactionView.test.ts | 30 +++++++++++++++++++ .../integration/transaction_view.spec.ts | 11 ++++++- .../enzyme/tests/TransactionCreate.test.tsx | 22 ++++++++++++++ .../TransactionCreate.test.tsx.snap | 1 + packages/blockchain-ui/src/Utils.ts | 5 +--- .../TransactionCreate/TransactionCreate.tsx | 5 ++-- 11 files changed, 113 insertions(+), 19 deletions(-) diff --git a/packages/blockchain-extension/extension/commands/openTransactionViewCommand.ts b/packages/blockchain-extension/extension/commands/openTransactionViewCommand.ts index ef51859a14..ecc64d0112 100644 --- a/packages/blockchain-extension/extension/commands/openTransactionViewCommand.ts +++ b/packages/blockchain-extension/extension/commands/openTransactionViewCommand.ts @@ -69,22 +69,27 @@ export async function openTransactionView(treeItem?: InstantiatedTreeItem): Prom const chaincodes: Array = await connection.getInstantiatedChaincode(thisChannelName); // returns array of objects for (const chaincode of chaincodes) { metadataObj = await connection.getMetadata(chaincode.name, thisChannelName); - contract = metadataObj.contracts[Object.keys(metadataObj.contracts)[0]]; - data = { - name: chaincode.name, - version: chaincode.version, - channel: thisChannelName, - label: chaincode.name + '@' + chaincode.version, - transactions: contract.transactions, - namespace: contract.name - }; - instantiatedChaincodes.push(data); + const contractsObject: any = metadataObj.contracts; + Object.keys(contractsObject).forEach((key: string) => { + if (key !== 'org.hyperledger.fabric' && (contractsObject[key].transactions.length > 0)) { + contract = metadataObj.contracts[key]; + data = { + name: chaincode.name, + version: chaincode.version, + channel: thisChannelName, + label: chaincode.name + '@' + chaincode.version, + transactions: contract.transactions, + namespace: contract.name + }; + instantiatedChaincodes.push(data); + } + }); } } const appState: {} = { smartContracts: instantiatedChaincodes, - activeSmartContract: instantiatedChaincodes.filter((obj: any) => obj.label === smartContractLabel)[0] + activeSmartContract: instantiatedChaincodes.find((obj: any) => obj.label === smartContractLabel) }; const context: vscode.ExtensionContext = GlobalState.getExtensionContext(); diff --git a/packages/blockchain-extension/extension/util/ExtensionUtil.ts b/packages/blockchain-extension/extension/util/ExtensionUtil.ts index db5fc5c621..c960140232 100644 --- a/packages/blockchain-extension/extension/util/ExtensionUtil.ts +++ b/packages/blockchain-extension/extension/util/ExtensionUtil.ts @@ -333,7 +333,7 @@ export class ExtensionUtil { context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.TEST_ALL_SMART_CONTRACT, (chaincode: InstantiatedContractTreeItem) => testSmartContract(true, chaincode))); context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.TEST_SMART_CONTRACT, (treeItem: ContractTreeItem | InstantiatedTreeItem) => testSmartContract(false, treeItem))); context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.SUBMIT_TRANSACTION, (transactionTreeItem?: InstantiatedTreeItem | TransactionTreeItem, channelName?: string, smartContract?: string, transactionObject?: any) => submitTransaction(false, transactionTreeItem, channelName, smartContract, transactionObject))); - context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.EVALUATE_TRANSACTION, (transactionTreeItem?: InstantiatedTreeItem | TransactionTreeItem, channelName?: string, smartContract?: string) => submitTransaction(true, transactionTreeItem, channelName, smartContract))); + context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.EVALUATE_TRANSACTION, (transactionTreeItem?: InstantiatedTreeItem | TransactionTreeItem, channelName?: string, smartContract?: string, transactionObject?: any) => submitTransaction(true, transactionTreeItem, channelName, smartContract, transactionObject))); context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.UPGRADE_SMART_CONTRACT, (instantiatedChainCodeTreeItem?: InstantiatedTreeItem, channelName?: string, peerNames?: Array) => upgradeSmartContract(instantiatedChainCodeTreeItem, channelName, peerNames))); context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.CREATE_NEW_IDENTITY, (certificateAuthorityTreeItem?: CertificateAuthorityTreeItem) => createNewIdentity(certificateAuthorityTreeItem))); context.subscriptions.push(vscode.commands.registerCommand(ExtensionCommands.REFRESH_WALLETS, (element: BlockchainTreeItem) => blockchainWalletExplorerProvider.refresh(element))); diff --git a/packages/blockchain-extension/extension/webview/TransactionView.ts b/packages/blockchain-extension/extension/webview/TransactionView.ts index 01d4f24009..0f0ad9475b 100644 --- a/packages/blockchain-extension/extension/webview/TransactionView.ts +++ b/packages/blockchain-extension/extension/webview/TransactionView.ts @@ -29,6 +29,8 @@ export class TransactionView extends ReactView { panel.webview.onDidReceiveMessage(async (message: {command: string, data: any}) => { if (message.command === 'submit') { await vscode.commands.executeCommand(ExtensionCommands.SUBMIT_TRANSACTION, undefined, undefined, undefined, message.data); + } else if (message.command === 'evaluate') { + await vscode.commands.executeCommand(ExtensionCommands.EVALUATE_TRANSACTION, undefined, undefined, undefined, message.data); } }); this.loadComponent(panel); diff --git a/packages/blockchain-extension/test/commands/openTransactionViewCommand.test.ts b/packages/blockchain-extension/test/commands/openTransactionViewCommand.test.ts index ee867c927d..d1a697695d 100644 --- a/packages/blockchain-extension/test/commands/openTransactionViewCommand.test.ts +++ b/packages/blockchain-extension/test/commands/openTransactionViewCommand.test.ts @@ -108,6 +108,14 @@ describe('OpenTransactionViewCommand', () => { name: 'transaction2' } ], + }, + 'org.hyperledger.fabric': { + name: 'org.hyperledger.fabric', + transactions: [ + { + name: 'GetMetadata' + } + ] } } } diff --git a/packages/blockchain-extension/test/commands/submitTransaction.test.ts b/packages/blockchain-extension/test/commands/submitTransaction.test.ts index e8373f3cee..bc1fc34c55 100644 --- a/packages/blockchain-extension/test/commands/submitTransaction.test.ts +++ b/packages/blockchain-extension/test/commands/submitTransaction.test.ts @@ -626,5 +626,24 @@ describe('SubmitTransactionCommand', () => { logSpy.should.have.been.calledWith(LogType.SUCCESS, 'Successfully submitted transaction'); reporterStub.should.have.been.calledWith('submit transaction'); }); + + it('should evaluate a transaction through the transaction view', async () => { + const transactionObject: any = { + smartContract: 'myContract', + transactionName: 'transaction1', + channelName: 'myChannel', + args: `["arg1", "arg2", "arg3"]`, + namespace: 'my-contract', + transientData: '', + peerTargetNames: [] + }; + + await vscode.commands.executeCommand(ExtensionCommands.EVALUATE_TRANSACTION, undefined, undefined, undefined, transactionObject); + fabricClientConnectionMock.submitTransaction.should.have.been.calledWith('myContract', 'transaction1', 'myChannel', ['arg1', 'arg2', 'arg3'], 'my-contract'); + dockerLogsOutputSpy.should.not.have.been.called; + logSpy.should.have.been.calledWith(LogType.INFO, undefined, `evaluating transaction transaction1 with args arg1,arg2,arg3 on channel myChannel`); + logSpy.should.have.been.calledWith(LogType.SUCCESS, 'Successfully evaluated transaction'); + reporterStub.should.have.been.calledWith('evaluate transaction'); + }); }); }); diff --git a/packages/blockchain-extension/test/webview/TransactionView.test.ts b/packages/blockchain-extension/test/webview/TransactionView.test.ts index d48128fa2a..f5923c5ce1 100644 --- a/packages/blockchain-extension/test/webview/TransactionView.test.ts +++ b/packages/blockchain-extension/test/webview/TransactionView.test.ts @@ -166,6 +166,36 @@ describe('TransactionView', () => { executeCommandStub.should.have.been.calledWith(ExtensionCommands.SUBMIT_TRANSACTION, undefined, undefined, undefined, transactionObject); }); + it(`should handle an 'evaluate' message`, async () => { + const onDidReceiveMessagePromises: any[] = []; + + onDidReceiveMessagePromises.push(new Promise((resolve: any): void => { + createWebviewPanelStub.onCall(0).returns({ + webview: { + postMessage: mySandBox.stub(), + onDidReceiveMessage: async (callback: any): Promise => { + await callback({ + command: 'evaluate', + data: transactionObject + }); + resolve(); + } + }, + reveal: (): void => { + return; + }, + onDidDispose: mySandBox.stub(), + onDidChangeViewState: mySandBox.stub() + }); + })); + + const transactionView: TransactionView = new TransactionView(context, mockAppState); + await transactionView.openView(false); + await Promise.all(onDidReceiveMessagePromises); + + executeCommandStub.should.have.been.calledWith(ExtensionCommands.EVALUATE_TRANSACTION, undefined, undefined, undefined, transactionObject); + }); + it('should not do anything if it receives an invalid message', async () => { const onDidReceiveMessagePromises: any[] = []; diff --git a/packages/blockchain-ui/cypress/integration/transaction_view.spec.ts b/packages/blockchain-ui/cypress/integration/transaction_view.spec.ts index 2903c86768..93b75f6b88 100644 --- a/packages/blockchain-ui/cypress/integration/transaction_view.spec.ts +++ b/packages/blockchain-ui/cypress/integration/transaction_view.spec.ts @@ -154,7 +154,7 @@ describe('Cypress', () => { }); }); - it('can submit a transaction with the user\'s input', () => { + it(`can submit a transaction with the user's input`, () => { cy.get('#transaction-name-select').select('transactionOne'); cy.get('#arguments-text-area').type('{leftarrow}{leftarrow}{leftarrow}penguin'); @@ -162,5 +162,14 @@ describe('Cypress', () => { cy.get('@postMessageStub').should('be.called'); }); + + it(`can evaluate a transaction with the user's input`, () => { + cy.get('#transaction-name-select').select('transactionTwo'); + cy.get('#arguments-text-area').type('{leftarrow}{leftarrow}{leftarrow}big'); + + cy.get('#evaluate-button').click(); + + cy.get('@postMessageStub').should('be.called'); + }); }); }); diff --git a/packages/blockchain-ui/enzyme/tests/TransactionCreate.test.tsx b/packages/blockchain-ui/enzyme/tests/TransactionCreate.test.tsx index bfb0837593..43dcbccf53 100644 --- a/packages/blockchain-ui/enzyme/tests/TransactionCreate.test.tsx +++ b/packages/blockchain-ui/enzyme/tests/TransactionCreate.test.tsx @@ -128,6 +128,28 @@ describe('TransactionCreate component', () => { }); }); + it('should attempt to evaluate a transaction when the evaluate button is clicked', async () => { + const component: any = mount(); + component.setState({ + activeTransaction: transactionOne, + transactionArguments: 'name: Green\n' + }); + component.find('#evaluate-button').at(1).simulate('click'); + postMessageHandlerStub.should.have.been.calledOnceWithExactly({ + data: { + args: 'Green', + channelName: 'mychannel', + evaluate: true, + namespace: 'GreenContract', + peerTargetNames: [], + smartContract: 'greenContract', + transactionName: 'transactionOne', + transientData: '' + }, + command: 'evaluate' + }); + }); + it('should do nothing if no transaction has been selected', async () => { const component: any = mount(); component.find('#submit-button').at(1).simulate('click'); diff --git a/packages/blockchain-ui/enzyme/tests/__snapshots__/TransactionCreate.test.tsx.snap b/packages/blockchain-ui/enzyme/tests/__snapshots__/TransactionCreate.test.tsx.snap index 2bac0a717b..d26d5990f3 100644 --- a/packages/blockchain-ui/enzyme/tests/__snapshots__/TransactionCreate.test.tsx.snap +++ b/packages/blockchain-ui/enzyme/tests/__snapshots__/TransactionCreate.test.tsx.snap @@ -395,6 +395,7 @@ exports[`TransactionCreate component should render the expected snapshot 1`] = ` className="bx--btn bx--btn--field bx--btn--primary bx--btn--disabled" disabled={true} id="evaluate-button" + onClick={[Function]} tabIndex={0} type="button" > diff --git a/packages/blockchain-ui/src/Utils.ts b/packages/blockchain-ui/src/Utils.ts index 7778b29b96..2d6c2f5852 100644 --- a/packages/blockchain-ui/src/Utils.ts +++ b/packages/blockchain-ui/src/Utils.ts @@ -12,10 +12,7 @@ const Utils: any = { }, postToVSCode(message: {command: string, data: any}): void { - vscode.postMessage({ - command: message.command, - data: message.data - }); + vscode.postMessage(message); } }; diff --git a/packages/blockchain-ui/src/components/TransactionCreate/TransactionCreate.tsx b/packages/blockchain-ui/src/components/TransactionCreate/TransactionCreate.tsx index eaedb43f1e..12225be888 100644 --- a/packages/blockchain-ui/src/components/TransactionCreate/TransactionCreate.tsx +++ b/packages/blockchain-ui/src/components/TransactionCreate/TransactionCreate.tsx @@ -74,10 +74,11 @@ class TransactionCreate extends Component { submitTxn(evaluate: boolean): void { const activeTransaction: ITransaction = this.state.activeTransaction as ITransaction; + const command: string = evaluate ? 'evaluate' : 'submit'; const args: string = this.parseArgs(activeTransaction, this.state.transactionArguments); const transactionObject: any = { - command: 'submit', + command: command, data: { smartContract: this.state.activeSmartContract.name, transactionName: activeTransaction.name, @@ -159,7 +160,7 @@ class TransactionCreate extends Component {
- +