'use strict' ; import * as vscode from 'vscode' ; import * as path from 'path' ; import { PeerTreeItem } from './runtimeOps/connectedTree/PeerTreeItem' ; import { ChannelTreeItem } from './model/ChannelTreeItem' ; import { BlockchainTreeItem } from './model/BlockchainTreeItem' ; import { ImportNodesTreeItem } from './runtimeOps/connectedTree/ImportNodesTreeItem' ; import { BlockchainExplorerProvider } from './BlockchainExplorerProvider' ; import { RuntimeTreeItem } from './runtimeOps/disconnectedTree/RuntimeTreeItem' ; import { VSCodeBlockchainOutputAdapter } from '../logging/VSCodeBlockchainOutputAdapter' ; import { NodesTreeItem } from './runtimeOps/connectedTree/NodesTreeItem' ; import { OrganizationsTreeItem } from './runtimeOps/connectedTree/OrganizationsTreeItem' ; import { OrgTreeItem } from './runtimeOps/connectedTree/OrgTreeItem' ; import { ExtensionCommands } from '../../ExtensionCommands' ; import { CertificateAuthorityTreeItem } from './runtimeOps/connectedTree/CertificateAuthorityTreeItem' ; import { OrdererTreeItem } from './runtimeOps/connectedTree/OrdererTreeItem' ; import { FabricEnvironmentManager , ConnectedState } from '../fabric/environments/FabricEnvironmentManager' ; import { FabricEnvironmentRegistry , FabricEnvironmentRegistryEntry , FabricNode , FabricNodeType , FabricRuntimeUtil , IFabricEnvironmentConnection , LogType , FabricEnvironment , EnvironmentType , FabricSmartContractDefinition } from 'ibm-blockchain-platform-common' ; import { FabricEnvironmentTreeItem } from './runtimeOps/disconnectedTree/FabricEnvironmentTreeItem' ; import { SetupTreeItem } from './runtimeOps/identitySetupTree/SetupTreeItem' ; import { EnvironmentConnectedTreeItem } from './runtimeOps/connectedTree/EnvironmentConnectedTreeItem' ; import { TextTreeItem } from './model/TextTreeItem' ; import { EnvironmentFactory } from '../fabric/environments/EnvironmentFactory' ; import { EditFiltersTreeItem } from './runtimeOps/connectedTree/EditFiltersTreeItem' ; import { CommittedContractTreeItem } from './runtimeOps/connectedTree/CommittedSmartContractTreeItem' ; import { DeployTreeItem } from './runtimeOps/connectedTree/DeployTreeItem' ; import { EnvironmentGroupTreeItem } from './runtimeOps/EnvironmentGroupTreeItem' ; import { ExtensionsInteractionUtil } from '../util/ExtensionsInteractionUtil' ; import { SettingConfigurations } from '../configurations' ; import { LocalMicroEnvironment } from '../fabric/environments/LocalMicroEnvironment' ; import { LocalMicroEnvironmentManager } from '../fabric/environments/LocalMicroEnvironmentManager' ; export class BlockchainEnvironmentExplorerProvider implements BlockchainExplorerProvider { public tree : Array < BlockchainTreeItem > = [ ] ; private _onDidChangeTreeData : vscode . EventEmitter < any | undefined > = new vscode . EventEmitter < any | undefined > ( ) ; readonly onDidChangeTreeData : vscode . Event < any | undefined > = this . _onDidChangeTreeData . event ; constructor ( ) { FabricEnvironmentManager . instance ( ) . on ( 'connected' , async ( ) => { await this . connect ( ) ; } ) ; FabricEnvironmentManager . instance ( ) . on ( 'disconnected' , async ( ) => { await this . disconnect ( ) ; } ) ; } async refresh ( element ? ) { this . _onDidChangeTreeData . fire ( element ) ; } async connect ( ) { await this . refresh ( ) ; } async disconnect ( ) { await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-connected' , false ) ; await this . refresh ( ) ; } getTreeItem ( element ) { return element ; } async getChildren ( element ? ) { if ( element ) { if ( element instanceof EnvironmentGroupTreeItem ) { this . tree = await this . populateEnvironments ( element . environments ) ; } if ( element instanceof ChannelTreeItem ) { this . tree = await this . createCommittedTree ( element ) ; } if ( element instanceof NodesTreeItem ) { this . tree = await this . createNodesTree ( ) ; } if ( element instanceof OrganizationsTreeItem ) { this . tree = await this . createOrganizationsTree ( ) ; } } else if ( FabricEnvironmentManager . instance ( ) . getState ( ) === ConnectedState . SETUP ) { await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-setup' , true ) ; const environmentRegistryEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; this . tree = await this . setupIdentities ( environmentRegistryEntry ) ; } else if ( FabricEnvironmentManager . instance ( ) . getState ( ) === ConnectedState . CONNECTING || FabricEnvironmentManager . instance ( ) . getState ( ) === ConnectedState . CONNECTED ) { const environmentRegistryEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; if ( environmentRegistryEntry . managedRuntime ) { await vscode . commands . executeCommand ( 'setContext' , 'blockchain-runtime-connected' , true ) ; } else if ( environmentRegistryEntry . environmentType === EnvironmentType . MICROFAB_ENVIRONMENT ) { await vscode . commands . executeCommand ( 'setContext' , 'blockchain-microfab-connected' , true ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-runtime-connected' , false ) ; } else { if ( environmentRegistryEntry . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || environmentRegistryEntry . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { if ( FabricEnvironmentManager . instance ( ) . getState ( ) === ConnectedState . CONNECTED ) { await vscode . commands . executeCommand ( ExtensionCommands . CONNECT_TO_ENVIRONMENT , environmentRegistryEntry ) ; if ( FabricEnvironmentManager . instance ( ) . getState ( ) !== ConnectedState . DISCONNECTED ) { this . tree = await this . createConnectedTree ( environmentRegistryEntry ) ; } else { this . tree = await this . createConnectionTree ( ) ; } return this . tree ; } await vscode . commands . executeCommand ( 'setContext' , 'blockchain-opstool-connected' , true ) ; } await vscode . commands . executeCommand ( 'setContext' , 'blockchain-runtime-connected' , false ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-microfab-connected' , false ) ; } await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-connected' , true ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-setup' , false ) ; this . tree = await this . createConnectedTree ( environmentRegistryEntry ) ; if ( FabricEnvironmentManager . instance ( ) . getState ( ) === ConnectedState . CONNECTING ) { FabricEnvironmentManager . instance ( ) . setState ( ConnectedState . CONNECTED ) ; } } else { await vscode . commands . executeCommand ( 'setContext' , 'blockchain-opstool-connected' , false ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-setup' , false ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-runtime-connected' , false ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-environment-connected' , false ) ; await vscode . commands . executeCommand ( 'setContext' , 'blockchain-microfab-connected' , false ) ; this . tree = await this . createConnectionTree ( ) ; } return this . tree ; } private async setupIdentities ( environmentRegistryEntry ) { const tree = [ ] ; const environment = EnvironmentFactory . getEnvironment ( environmentRegistryEntry ) ; const nodes = await environment . getNodes ( true ) ; tree . push ( new SetupTreeItem ( this , `Setting up: ${ environmentRegistryEntry . name }` ) ) ; tree . push ( new SetupTreeItem ( this , ( '(Click each node to perform setup)' ) ) ) ; for ( const node of nodes ) { const command = { command : ExtensionCommands . ASSOCIATE_IDENTITY_NODE , title : '' , arguments : [ environmentRegistryEntry , node ] } ; let label = node . cluster_name || node . name ; if ( ! node . wallet || ! node . identity ) { label += '   ⚠' ; } if ( node . type === FabricNodeType . PEER ) { const peerTreeItem = new PeerTreeItem ( this , label , node . name , environmentRegistryEntry , node , command ) ; tree . push ( peerTreeItem ) ; } if ( node . type === FabricNodeType . CERTIFICATE_AUTHORITY ) { const certificateAuthorityTreeItem = new CertificateAuthorityTreeItem ( this , label , node . name , environmentRegistryEntry , node , command ) ; tree . push ( certificateAuthorityTreeItem ) ; } if ( node . type === FabricNodeType . ORDERER ) { if ( node . cluster_name ) { const foundTreeItem = tree . find ( ( treeItem ) => treeItem . node && treeItem . node . type === FabricNodeType . ORDERER && node . cluster_name === treeItem . node . cluster_name ) ; if ( ! foundTreeItem ) { tree . push ( new OrdererTreeItem ( this , label , node . cluster_name , environmentRegistryEntry , node , command ) ) ; } } else { tree . push ( new OrdererTreeItem ( this , label , node . name , environmentRegistryEntry , node , command ) ) ; } } } return tree ; } private async createConnectionTree ( ) { const tree = [ ] ; const environmentEntries = await FabricEnvironmentRegistry . instance ( ) . getAll ( ) ; const environmentGroups = [ ] ; const cloudEnvironments = [ ] ; const otherEnvironments = [ ] ; for ( const environment of environmentEntries ) { if ( environmentGroups . length === 0 ) { if ( environment . environmentType === EnvironmentType . LOCAL_MICROFAB_ENVIRONMENT ) { environmentGroups . push ( [ environment ] ) ; } else if ( environment . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || environment . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { cloudEnvironments . push ( environment ) ; } else { otherEnvironments . push ( environment ) ; } continue ; } const groupIndex = environmentGroups . findIndex ( ( group ) => { return group [ 0 ] . environmentType === environment . environmentType ; } ) ; if ( groupIndex !== - 1 ) { environmentGroups [ groupIndex ] . push ( environment ) ; } else { if ( environment . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || environment . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { cloudEnvironments . push ( environment ) ; } else { otherEnvironments . push ( environment ) ; } } } if ( cloudEnvironments . length > 0 ) { environmentGroups . push ( cloudEnvironments ) ; } if ( otherEnvironments . length > 0 ) { environmentGroups . push ( otherEnvironments ) ; } if ( environmentGroups . length === 0 ) { const command = { command : ExtensionCommands . ADD_ENVIRONMENT , title : '' , arguments : [ ] } ; tree . push ( new TextTreeItem ( this , '+ Add local or remote environment' , command ) ) ; const treeItem = await this . getIBMCloudInteractionItem ( true ) as EnvironmentGroupTreeItem ; if ( treeItem ) { tree . push ( treeItem ) ; } } else { for ( const group of environmentGroups ) { let groupName = '' ; if ( group [ 0 ] . environmentType === EnvironmentType . LOCAL_MICROFAB_ENVIRONMENT ) { groupName = 'Simple local networks' ; } else if ( group [ 0 ] . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || group [ 0 ] . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { groupName = 'IBM Blockchain Platform on cloud' ; } else { groupName = 'Other networks' ; } tree . push ( new EnvironmentGroupTreeItem ( this , groupName , group , vscode . TreeItemCollapsibleState . Expanded ) ) ; } if ( ! tree . some ( ( treeItem ) => treeItem . label === 'IBM Blockchain Platform on cloud' ) ) { const treeItem = await this . getIBMCloudInteractionItem ( true ) as EnvironmentGroupTreeItem ; if ( treeItem ) { tree . push ( treeItem ) ; tree . sort ( ( a , b ) : number => { if ( a . label !== 'Simple local networks' && b . label !== 'Simple local networks' ) { return a . label . localeCompare ( b . label ) ; } } ) ; } } } return tree ; } private async populateEnvironments ( environments ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; try { const tree = [ ] ; if ( environments . length === 0 ) { tree . push ( await this . getIBMCloudInteractionItem ( false ) as TextTreeItem ) ; } else { if ( environments [ 0 ] . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT ) { if ( ! environments . some ( ( environment ) => environment . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) ) { const treeItem = await this . getIBMCloudInteractionItem ( false ) as TextTreeItem ; if ( treeItem ) { tree . push ( treeItem ) ; } } } for ( const environment of environments ) { if ( environment . managedRuntime ) { const runtime = await LocalMicroEnvironmentManager . instance ( ) . ensureRuntime ( environment . name , undefined , environment . numberOfOrgs ) ; const treeItem = await RuntimeTreeItem . newRuntimeTreeItem ( this , runtime . getName ( ) , environment , { command : ExtensionCommands . CONNECT_TO_ENVIRONMENT , title : '' , arguments : [ environment ] } , runtime ) ; const isRunning = await runtime . isRunning ( ) ; if ( isRunning ) { treeItem . contextValue = 'blockchain-runtime-item-running' ; } treeItem . iconPath = { light : path . join ( __filename , '..' , '..' , '..' , '..' , 'resources' , 'light' , 'laptop.svg' ) , dark : path . join ( __filename , '..' , '..' , '..' , '..' , 'resources' , 'dark' , 'laptop.svg' ) } ; tree . push ( treeItem ) ; } else { const environmentTreeItem = new FabricEnvironmentTreeItem ( this , environment . name , environment , { command : ExtensionCommands . CONNECT_TO_ENVIRONMENT , title : '' , arguments : [ environment ] } ) ; if ( environment . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || environment . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { environmentTreeItem . iconPath = { light : path . join ( __filename , '..' , '..' , '..' , '..' , 'resources' , 'light' , 'ibm-cloud.svg' ) , dark : path . join ( __filename , '..' , '..' , '..' , '..' , 'resources' , 'dark' , 'ibm-cloud.svg' ) } ; environmentTreeItem . contextValue = 'blockchain-ops-environment' ; } else if ( environment . environmentType === EnvironmentType . MICROFAB_ENVIRONMENT ) { environmentTreeItem . contextValue = 'blockchain-microfab-item' ; } tree . push ( environmentTreeItem ) ; } } } return tree ; } catch ( error ) { outputAdapter . log ( LogType . ERROR , `Error populating Fabric Environment Panel: ${ error . message }` , `Error populating Fabric Environment Panel: ${ error . toString ( ) }` ) ; } } private async getIBMCloudInteractionItem ( returnGroupItem ) { const ibmCloudExtensionInstalled = ExtensionsInteractionUtil . isIBMCloudExtensionInstalled ( ) ; if ( ! ibmCloudExtensionInstalled ) { if ( returnGroupItem ) { return new EnvironmentGroupTreeItem ( this , 'IBM Blockchain Platform on cloud' , [ ] , vscode . TreeItemCollapsibleState . Expanded ) ; } else { const label = '+ Install IBM Cloud Account extension' ; const command = { command : ExtensionCommands . OPEN_VSCODE_EXTENSION , title : '' , arguments : [ ] } ; return new TextTreeItem ( this , label , command ) ; } } const isLoggedIn = await ExtensionsInteractionUtil . cloudAccountIsLoggedIn ( ) ; const hasAccountSelected = await ExtensionsInteractionUtil . cloudAccountHasSelectedAccount ( ) ; if ( ! isLoggedIn || ! hasAccountSelected ) { if ( returnGroupItem ) { return new EnvironmentGroupTreeItem ( this , 'IBM Blockchain Platform on cloud' , [ ] , vscode . TreeItemCollapsibleState . Expanded ) ; } else { const label = ! isLoggedIn ? '+ Log in to IBM Cloud' : '+ Select IBM Cloud account' ; const command = { command : ExtensionCommands . LOG_IN_AND_DISCOVER , title : '' , arguments : [ ] } ; return new TextTreeItem ( this , label , command ) ; } } else { const anyIbpResources = await ExtensionsInteractionUtil . cloudAccountAnyIbpResources ( ) ; if ( anyIbpResources ) { const shouldDiscover = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . DISCOVER_SAAS_ENVS ) ; if ( shouldDiscover === true ) { await vscode . commands . executeCommand ( ExtensionCommands . LOG_IN_AND_DISCOVER ) ; } } else { if ( returnGroupItem ) { return new EnvironmentGroupTreeItem ( this , 'IBM Blockchain Platform on cloud' , [ ] , vscode . TreeItemCollapsibleState . Expanded ) ; } else { const command = { command : ExtensionCommands . OPEN_NEW_INSTANCE_LINK , title : '' , arguments : [ ] } ; return new TextTreeItem ( this , '+ create new instance' , command ) ; } } } } private async createConnectedTree ( environmentRegistryEntry ) { const tree = [ ] ; const name = environmentRegistryEntry . name ; tree . push ( new EnvironmentConnectedTreeItem ( this , `Connected to environment: ${ name }` ) ) ; const channels = await this . createChannelsTree ( ) ; tree . push ( ... channels ) ; tree . push ( new NodesTreeItem ( this , vscode . TreeItemCollapsibleState . Collapsed ) ) ; tree . push ( new OrganizationsTreeItem ( this , vscode . TreeItemCollapsibleState . Collapsed ) ) ; return tree ; } private async createChannelsTree ( ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const tree = [ ] ; const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; try { const channelMap = await connection . createChannelMap ( ) ; const channels = Array . from ( channelMap . keys ( ) ) ; for ( const channel of channels ) { const peers = channelMap . get ( channel ) ; const smartContracts = await connection . getCommittedSmartContractDefinitions ( peers , channel ) ; const capability = await connection . getChannelCapabilityFromPeer ( channel , peers [ 0 ] ) ; tree . push ( new ChannelTreeItem ( this , channel , peers , smartContracts , capability [ 0 ] , vscode . TreeItemCollapsibleState . Collapsed ) ) ; } } catch ( error ) { outputAdapter . log ( LogType . ERROR , `Error populating channel view: ${ error . message }` , `Error populating channel view: ${ error . toString ( ) }` ) ; return tree ; } return tree ; } private async createCommittedTree ( element ) { const tree = [ ] ; for ( const smartContract of element . chaincodes ) { tree . push ( new CommittedContractTreeItem ( this , `${ smartContract . name }@${ smartContract . version }` ) ) ; } const fabricEnvironmentRegistryEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; const command = { command : ExtensionCommands . OPEN_DEPLOY_PAGE , title : '' , arguments : [ fabricEnvironmentRegistryEntry , element . label ] } ; tree . push ( new DeployTreeItem ( this , command ) ) ; return tree ; } private async createNodesTree ( ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const tree = [ ] ; try { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; const environmentRegistryEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; const peerNames = connection . getAllPeerNames ( ) ; for ( const peerName of peerNames ) { const node = connection . getNode ( peerName ) ; const tooltip = `Name: ${ node . name }\nMSPID: ${ node . msp_id }\nAssociated Identity:\n${ node . identity }` ; const peerTreeItem = new PeerTreeItem ( this , peerName , tooltip , environmentRegistryEntry , node ) ; tree . push ( peerTreeItem ) ; } const certificateAuthorityNames = connection . getAllCertificateAuthorityNames ( ) ; for ( const certificateAuthorityName of certificateAuthorityNames ) { const node = connection . getNode ( certificateAuthorityName ) ; const tooltip = `Name: ${ node . name }\nAssociated Identity:\n${ node . identity }` ; const caTreeItem = new CertificateAuthorityTreeItem ( this , certificateAuthorityName , tooltip , environmentRegistryEntry , node ) ; tree . push ( caTreeItem ) ; } const ordererNames = connection . getAllOrdererNames ( ) ; for ( const ordererName of ordererNames ) { const node = connection . getNode ( ordererName ) ; if ( node . cluster_name ) { const foundTreeItem = tree . find ( ( treeItem ) => node . type === FabricNodeType . ORDERER && node . cluster_name === treeItem . node . cluster_name ) ; if ( ! foundTreeItem ) { const tooltip = `Name: ${ node . cluster_name }\nMSPID: ${ node . msp_id }\nAssociated Identity:\n${ node . identity }` ; tree . push ( new OrdererTreeItem ( this , node . cluster_name , tooltip , environmentRegistryEntry , node ) ) ; } } else { const tooltip = `Name: ${ node . name }\nMSPID: ${ node . msp_id }\nAssociated Identity:\n${ node . identity }` ; tree . push ( new OrdererTreeItem ( this , node . name , tooltip , environmentRegistryEntry , node ) ) ; } } const environmentEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; if ( environmentEntry . name !== FabricRuntimeUtil . LOCAL_FABRIC ) { if ( environmentEntry . environmentType === EnvironmentType . OPS_TOOLS_ENVIRONMENT || environmentEntry . environmentType === EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ) { tree . push ( new EditFiltersTreeItem ( this , { command : ExtensionCommands . EDIT_NODE_FILTERS , title : '' , arguments : [ environmentEntry ] , } ) ) ; } else { tree . push ( new ImportNodesTreeItem ( this , { command : ExtensionCommands . IMPORT_NODES_TO_ENVIRONMENT , title : '' , arguments : [ environmentEntry ] } ) ) ; } } } catch ( error ) { outputAdapter . log ( LogType . ERROR , `Error populating nodes view: ${ error . message }` , `Error populating nodes view: ${ error . toString ( ) }` ) ; return tree ; } return tree ; } private async createOrganizationsTree ( ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; const orgNames = connection . getAllOrganizationNames ( ) ; return orgNames . map ( ( organizationName ) => new OrgTreeItem ( this , organizationName ) ) ; } }