'use strict' ; import * as vscode from 'vscode' ; import * as fs from 'fs-extra' ; import * as path from 'path' ; import { FabricGatewayConnectionManager } from '../fabric/FabricGatewayConnectionManager' ; import { PackageRegistry } from '../registries/PackageRegistry' ; import { PackageRegistryEntry } from '../registries/PackageRegistryEntry' ; import { MetadataUtil } from '../util/MetadataUtil' ; import { VSCodeBlockchainOutputAdapter } from '../logging/VSCodeBlockchainOutputAdapter' ; import { FabricCertificate , FabricSmartContractDefinition , FabricInstalledSmartContract , FabricEnvironmentRegistry , FabricEnvironmentRegistryEntry , FabricNode , FabricNodeType , FabricWalletRegistry , FabricWalletRegistryEntry , IFabricEnvironmentConnection , IFabricGatewayConnection , LogType , FabricEnvironment , FabricGatewayRegistryEntry , FabricGatewayRegistry , EnvironmentFlags } from 'ibm-blockchain-platform-common' ; import { FabricEnvironmentManager } from '../fabric/environments/FabricEnvironmentManager' ; import { EnvironmentFactory } from '../fabric/environments/EnvironmentFactory' ; import { TimerUtil } from '../util/TimerUtil' ; import { ExtensionUtil } from '../util/ExtensionUtil' ; import { LocalMicroEnvironment } from '../fabric/environments/LocalMicroEnvironment' ; import { LocalMicroEnvironmentManager } from '../fabric/environments/LocalMicroEnvironmentManager' ; export interface IBlockchainQuickPickItem < T = undefined > extends vscode . QuickPickItem { data : T ; } export enum LanguageType { CHAINCODE = 'chaincode' , CONTRACT = 'contract' } export interface LanguageQuickPickItem extends vscode . QuickPickItem { type : LanguageType ; } export class UserInputUtil { static readonly ADD_TO_WORKSPACE : string = 'Add to workspace' ; static readonly OPEN_IN_CURRENT_WINDOW : string = 'Open in current window' ; static readonly OPEN_IN_NEW_WINDOW : string = 'Open in new window' ; static readonly YES : string = 'Yes' ; static readonly NO : string = 'No' ; static readonly DEFAULT : string = 'Default' ; static readonly CUSTOM : string = 'Custom' ; static readonly MORE_DETAILS : string = 'More Details' ; static readonly DEFAULT_SC_EP : string = `${ UserInputUtil . DEFAULT } (single endorser, any org)` ; static readonly OVERWRITE_FILE : string = 'Overwrite file' ; static readonly SKIP_FILE : string = 'Skip file' ; static readonly FORCE_FILES : string = 'Force all files to overwrite' ; static readonly ABORT_GENERATOR : string = 'Abort generator' ; static readonly BROWSE_LABEL : string = '📁 Browse' ; static readonly VALID_FOLDER_NAME : string = '- Folder can only contain alphanumeric, "-" and "_" characters' ; static readonly GENERATE_NEW_TEST_FILE : string = 'Generate new test file' ; static readonly IMPORT_WALLET : string = 'Specify an existing file system wallet' ; static readonly WALLET_NEW_ID : string = 'Create a new wallet and add an identity' ; static readonly WALLET : string = 'Create a new wallet' ; static readonly ADD_IDENTITY_METHOD : string = 'Choose a method for adding an identity' ; static readonly ADD_CERT_KEY_OPTION : string = 'Provide certificate and private key files' ; static readonly ADD_ID_SECRET_OPTION : string = 'Select a gateway and provide an enrollment ID and secret' ; static readonly ADD_LOCAL_ID_SECRET_OPTION : string = 'Provide an enrollment ID and secret' ; static readonly ADD_JSON_ID_OPTION : string = 'Provide a JSON identity file from IBM Blockchain Platform' ; static readonly ADD_MORE_NODES : string = 'Add more (JSON) node definitions' ; static readonly DONE_ADDING_NODES : string = 'Done adding nodes' ; static readonly ADD_IDENTITY : string = '+ Add identity' ; static readonly ADD_GATEWAY_FROM_ENVIRONMENT : string = 'Create a gateway from a Fabric environment' ; static readonly ADD_GATEWAY_FROM_CCP : string = 'Create a gateway from a connection profile' ; static readonly ADD_ENVIRONMENT_FROM_TEMPLATE : string = 'Create new from template' ; static readonly ADD_ENVIRONMENT_FROM_TEMPLATE_DESCRIPTION : string = '(uses Docker on your local machine)' ; static readonly ADD_ENVIRONMENT_FROM_NODES : string = 'Add any other Fabric network' ; static readonly ADD_ENVIRONMENT_FROM_NODES_DESCRIPTION : string = '(by providing node JSON files)' ; static readonly ADD_ENVIRONMENT_FROM_OPS_TOOLS : string = 'Add an IBM Blockchain Platform network' ; static readonly ADD_ENVIRONMENT_FROM_OPS_TOOLS_DESCRIPTION : string = '(connect to IBM Blockchain Platform Console)' ; static readonly ADD_ENVIRONMENT_FROM_MICROFAB : string = 'Add a Microfab network' ; static readonly ADD_ENVIRONMENT_FROM_MICROFAB_DESCRIPTION : string = '(connect to an instance of the Microfab runtime)' ; static readonly CANCEL_NO_CERT_CHAIN : string = 'Cancel' ; static readonly CANCEL_NO_CERT_CHAIN_DESCRIPTION : string = `(CA certificates must be added to the operating system trusted CA certificate store)` ; static readonly CONNECT_NO_CA_CERT_CHAIN : string = 'Proceed without certificate verification' ; static readonly GENERATE_DEFAULT_CONTRACT : string = 'Default Contract' ; static readonly GENERATE_DEFAULT_CONTRACT_DESCRIPTION : string = 'CRUD operations to a ledger shared by all network members' ; static readonly GENERATE_PD_CONTRACT : string = 'Private Data Contract' ; static readonly GENERATE_PD_CONTRACT_DESCRIPTION : string = 'CRUD and verify operations to a collection, private to a single network member' ; static readonly GENERATE_ENVIRONMENT_PROFILE : string = 'Environment profile' ; static readonly GENERATE_ENVIRONMENT_PROFILE_DESCRIPTION : string = '(describes connection profile, ID, channel and smart contract)' ; static readonly GENEARTE_SMART_CONTRACT_METADATA : string = 'Smart contract metadata' ; static readonly GENERATE_SMART_CONTRACT_METADATA_DESCRIPTION : string = '(describes transactions available to client applications)' ; static readonly ONE_ORG_TEMPLATE : string = `1 Org template (1 CA, 1 peer, 1 channel)` ; static readonly TWO_ORG_TEMPLATE : string = `2 Org template (2 CAs, 2 peers, 1 channel)` ; static readonly CREATE_ADDITIONAL_LOCAL_NETWORKS : string = `Create additional local networks (tutorial)` ; static readonly CREATE_ADDITIONAL_LOCAL_NETWORKS_DATA : number = 3 ; static readonly V1_4_2 : string = 'V1_4_2' ; static readonly V2_0 : string = 'V2_0' ; static readonly V2_0_RECOMMENDED : string = `${ UserInputUtil . V2_0 } (Recommended)` ; static readonly SELECT_PACKAGING_FABRIC_VERSION_1 : string = 'cds (V1 channel capabilities)' ; static readonly SELECT_PACKAGING_FABRIC_VERSION_2 : string = 'tar.gz (V2 channel capabilities)' ; public static async showQuickPick ( prompt , items , canPickMany = false ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; return vscode . window . showQuickPick ( items , quickPickOptions ) ; } public static async showQuickPickItem < T > ( prompt , items , canPickMany = false ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; return vscode . window . showQuickPick ( items , quickPickOptions ) ; } public static async showOrgQuickPick ( prompt , environmentRegistryEntry ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; const environment = EnvironmentFactory . getEnvironment ( environmentRegistryEntry ) ; let nodes = await environment . getNodes ( ) ; nodes = nodes . filter ( ( node ) => node . type === FabricNodeType . PEER ) ; const items = [ ] ; for ( const node of nodes ) { const found = items . some ( ( item ) => item . data . msp_id === node . msp_id ) ; if ( ! found ) { items . push ( { label : node . msp_id , data : node } ) ; } } if ( items . length === 0 ) { throw new Error ( 'No organisations found' ) ; } else if ( items . length === 1 ) { return items [ 0 ] ; } return vscode . window . showQuickPick ( items , quickPickOptions ) ; } public static async showFabricEnvironmentQuickPickBox ( prompt , canPickMany , autoChoose , includeFilter = [ ] , excludeFilter = [ ] , isRunning ? ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; const environments = await FabricEnvironmentRegistry . instance ( ) . getAll ( includeFilter , excludeFilter ) ; if ( isRunning !== undefined ) { for ( let i = environments . length - 1 ; i >= 0 ; i -- ) { const entry = environments [ i ] ; const _runtime = LocalMicroEnvironmentManager . instance ( ) . getRuntime ( entry . name ) ; const _isRuntimeRunning = await _runtime . isRunning ( ) ; if ( _isRuntimeRunning === isRunning ) { continue ; } else { environments . splice ( i , 1 ) ; } } } let environmentsQuickPickItems ; environmentsQuickPickItems = environments . map ( ( environment ) => { return { label : environment . name , data : environment } ; } ) ; if ( autoChoose ) { if ( environmentsQuickPickItems . length === 1 ) { return environmentsQuickPickItems [ 0 ] ; } } if ( environmentsQuickPickItems . length === 0 ) { throw new Error ( 'Error when choosing environment, no environments found to choose from.' ) ; } return vscode . window . showQuickPick ( environmentsQuickPickItems , quickPickOptions ) ; } public static async showGatewayQuickPickBox ( prompt , canPickMany , showManagedRuntime ? , showAssociatedGateways ? , fromEnvironment ? ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; const allGateways = [ ] ; let gateways = await FabricGatewayRegistry . instance ( ) . getAll ( showManagedRuntime ) ; if ( showAssociatedGateways !== undefined ) { gateways = gateways . filter ( ( gateway ) => { if ( showAssociatedGateways ) { return gateway . associatedWallet ; } else { return ! gateway . associatedWallet ; } } ) ; } if ( fromEnvironment ) { gateways = gateways . filter ( ( gateway ) => { return gateway . fromEnvironment === fromEnvironment ; } ) ; } allGateways . push ( ... gateways ) ; if ( allGateways . length === 0 ) { throw new Error ( 'Error when choosing gateway, no gateway found to choose from.' ) ; } const gatewaysQuickPickItems = allGateways . map ( ( gateway ) => { const gatewayDisplayName = gateway . name ; return { label : gatewayDisplayName , data : gateway } ; } ) ; return vscode . window . showQuickPick ( gatewaysQuickPickItems , quickPickOptions ) ; } public static showInputBox ( question , defaultValue ? ) { const inputBoxOptions = { prompt : question , ignoreFocusOut : true , value : defaultValue , valueSelection : [ 0 , 0 ] } ; return vscode . window . showInputBox ( inputBoxOptions ) ; } public static async addMoreNodes ( prompt ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; const quickPickItems = [ UserInputUtil . ADD_MORE_NODES , UserInputUtil . DONE_ADDING_NODES ] ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showWorkspaceQuickPickBox ( prompt ) { const workspaceFolderOptions = UserInputUtil . getWorkspaceFolders ( ) ; const workspaceQuickPickItems = workspaceFolderOptions . map ( ( workspaceFolderOption ) => { return { label : workspaceFolderOption . name , data : workspaceFolderOption , description : workspaceFolderOption . uri . fsPath } ; } ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , matchOnDetail : true , placeHolder : prompt } ; return vscode . window . showQuickPick ( workspaceQuickPickItems , quickPickOptions ) ; } returns { < Array < vscode . WorkspaceFolder > public static getWorkspaceFolders ( ) { const workspace = vscode . workspace . workspaceFolders || [ ] ; return workspace ; } public static showIdentitiesQuickPickBox ( prompt , canPickMany , identities , showCreate = false ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; if ( showCreate ) { identities . push ( UserInputUtil . ADD_IDENTITY ) ; } return vscode . window . showQuickPick ( identities , quickPickOptions ) ; } public static showFolderOptions ( prompt ) { const options = [ this . ADD_TO_WORKSPACE , this . OPEN_IN_NEW_WINDOW , this . OPEN_IN_CURRENT_WINDOW ] ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( options , quickPickOptions ) ; } public static showQuickPickYesNo ( prompt ) { const options = [ this . NO , this . YES ] ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( options , quickPickOptions ) ; } public static showQuickPickCA ( listOfCAs ) { const prompt = 'Choose your desired CA from the list' ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( listOfCAs , quickPickOptions ) ; } public static async showPeersQuickPickBox ( prompt , peerNames ? ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } if ( ! peerNames ) { peerNames = connection . getAllPeerNames ( ) ; } if ( peerNames . length > 1 ) { return vscode . window . showQuickPick ( peerNames , { ignoreFocusOut : true , canPickMany : true , placeHolder : prompt } ) ; } else { return peerNames ; } } public static getPackageFileExtension ( packagePath ) { return packagePath . endsWith ( '.tar.gz' ) ? '.tar.gz' : path . extname ( packagePath ) ; } public static async showChaincodeAndVersionQuickPick ( prompt , channel , peers , contractName ? , contractVersion ? ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } const tempQuickPickItems = [ ] ; const installedContracts = await this . getInstalledContracts ( connection , peers , true ) ; tempQuickPickItems . push ( ... installedContracts ) ; let packagedContracts = await this . getPackagedContracts ( ) ; packagedContracts = packagedContracts . filter ( ( _package ) => { const existingPackage = tempQuickPickItems . find ( ( item ) => { return item . label === _package . label ; } ) ; return ( ( ! existingPackage ) ? true : false ) ; } ) ; tempQuickPickItems . push ( ... packagedContracts ) ; const openProjects = await this . getOpenProjects ( ) ; tempQuickPickItems . push ( ... openProjects ) ; const allSmartContracts = [ ] ; const instantiatedSmartContracts = await connection . getCommittedSmartContractDefinitions ( peers , channel ) ; allSmartContracts . push ( ... instantiatedSmartContracts ) ; const quickPickItems = [ ] ; for ( const tempItem of tempQuickPickItems ) { const workspace = tempItem . data . workspace ; if ( ! tempItem . data . packageEntry ) { const data = { packageEntry : undefined , workspace : workspace } ; quickPickItems . push ( { label : `${ tempItem . data . workspace . name }` , description : tempItem . description , data } ) ; } else { const itemName = tempItem . data . packageEntry . name ; const itemVersion = tempItem . data . packageEntry . version ; let itemExtension = '' ; const packageEntryPath = tempItem . data . packageEntry . path || undefined ; if ( packageEntryPath ) { itemExtension = this . getPackageFileExtension ( packageEntryPath ) ; } const label = `${ itemName }@${ itemVersion }${ itemExtension }` ; if ( contractName && contractVersion ) { if ( itemName === contractName && itemVersion !== contractVersion ) { const data = { packageEntry : tempItem . data . packageEntry , workspace : workspace } ; quickPickItems . push ( { label , description : tempItem . description , data } ) ; } } else { const searchResult = allSmartContracts . findIndex ( ( contract ) => { return itemName === contract . name ; } ) ; if ( searchResult === - 1 ) { const data = { packageEntry : tempItem . data . packageEntry , workspace : workspace } ; quickPickItems . push ( { label , description : tempItem . description , data } ) ; } } } } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; if ( quickPickItems . length === 0 ) { throw new Error ( 'No contracts found' ) ; } else { return await vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } } public static async showV1ChannelQuickPickBox ( prompt ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } const channelMap = await connection . createChannelMap ( ) ; const channelMapv1 = new Map ( ) ; for ( const channel of Array . from ( channelMap . keys ( ) ) ) { const capabilities = await connection . getChannelCapabilityFromPeer ( channel , channelMap . get ( channel ) [ 0 ] ) ; if ( ! capabilities . includes ( 'V2_0' ) ) { channelMapv1 . set ( 'channel' , channelMap . get ( channel ) ) ; } } if ( channelMapv1 . size === 0 ) { throw new Error ( 'There are no channels with V1 capabilities enabled in this environment' ) ; } const chosenChannel = await this . showChannelQuickPickBox ( prompt , channelMapv1 ) ; return chosenChannel ; } public static async showChannelQuickPickBox ( prompt , channelMap ? ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } let channels = [ ] ; if ( ! channelMap ) { channelMap = await connection . createChannelMap ( ) ; } channels = Array . from ( channelMap . keys ( ) ) ; const quickPickItems = channels . map ( ( channel ) => { return { label : channel , data : channelMap . get ( channel ) } ; } ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; if ( quickPickItems . length > 1 ) { return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } else { return quickPickItems [ 0 ] ; } } public static async showChannelFromGatewayQuickPickBox ( prompt ) { const connection = FabricGatewayConnectionManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , 'Must be connected to a gateway' ) ; return ; } const channelMap = await connection . createChannelMap ( ) ; const channels = Array . from ( channelMap . keys ( ) ) ; const quickPickItems = channels . map ( ( channel ) => { return { label : channel , data : channelMap . get ( channel ) } ; } ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; if ( quickPickItems . length > 1 ) { return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } else { return quickPickItems [ 0 ] ; } } public static async showSmartContractPackagesQuickPickBox ( prompt , canPickMany ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const packages = await PackageRegistry . instance ( ) . getAll ( ) ; if ( packages . length === 0 ) { outputAdapter . log ( LogType . ERROR , 'There are no open packages.' ) ; return ; } const quickPickItems = packages . map ( ( _package ) => { const fileName = path . basename ( _package . path ) ; return { label : fileName , data : _package } ; } ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showLanguagesQuickPick ( prompt , chaincodeLanguages , contractLanguages ) { const choseLanguageQuickPickOptions = { placeHolder : prompt , ignoreFocusOut : true , matchOnDetail : true } ; chaincodeLanguages . sort ( ) ; const chaincodeQuickPickItems = chaincodeLanguages . filter ( ( chaincodeLanguage ) => contractLanguages . indexOf ( chaincodeLanguage ) === - 1 ) . map ( ( chaincodeLanguage ) => { return { label : chaincodeLanguage , description : 'Low-level programming model' , type : LanguageType . CHAINCODE } ; } ) ; contractLanguages . sort ( ) ; const contractQuickPickItems = contractLanguages . map ( ( contractLanguage ) => { return { label : contractLanguage , type : LanguageType . CONTRACT } ; } ) ; const quickPickItems = contractQuickPickItems . concat ( chaincodeQuickPickItems ) ; const chosenItem = await vscode . window . showQuickPick < LanguageQuickPickItem > ( quickPickItems , choseLanguageQuickPickOptions ) ; if ( chosenItem ) { return chosenItem ; } else { return undefined ; } } public static showGeneratorOptions ( prompt ) { const options = [ this . OVERWRITE_FILE , this . SKIP_FILE , this . FORCE_FILES , this . ABORT_GENERATOR ] ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( options , quickPickOptions ) ; } public static async showConfirmationWarningMessage ( prompt ) { const reallyDoIt = await vscode . window . showWarningMessage ( prompt , { title : 'Yes' } , { title : 'No' } ) ; if ( ! reallyDoIt ) { return false ; } return reallyDoIt . title === 'Yes' ; } public static async showClientInstantiatedSmartContractsQuickPick ( prompt , channelName ? , showAssociated ? ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const connection = FabricGatewayConnectionManager . instance ( ) . getConnection ( ) ; const channelMap = await connection . createChannelMap ( ) ; let instantiatedChaincodes = [ ] ; for ( const [ thisChannelName ] of channelMap ) { if ( channelName && ( channelName !== thisChannelName ) ) { continue ; } const chaincodes = await connection . getInstantiatedChaincode ( thisChannelName ) ; for ( const chaincode of chaincodes ) { const data = { name : chaincode . name , version : chaincode . version , sequence : chaincode . sequence , channel : thisChannelName } ; instantiatedChaincodes . push ( data ) ; } } if ( showAssociated === true ) { const gateway = await FabricGatewayConnectionManager . instance ( ) . getGatewayRegistryEntry ( ) ; const transactionDataDirectories = gateway . transactionDataDirectories ; if ( transactionDataDirectories === undefined || transactionDataDirectories . length === 0 ) { outputAdapter . log ( LogType . ERROR , 'No smart contracts with associations to transaction data directories found' ) ; return ; } else { const tempChaincodes = instantiatedChaincodes . filter ( ( chaincode ) => { return transactionDataDirectories . some ( ( directory ) => { return directory . chaincodeName === chaincode . name ; } ) ; } ) ; instantiatedChaincodes = tempChaincodes ; } } if ( instantiatedChaincodes . length === 0 ) { const gatewayRegistryEntry = await FabricGatewayConnectionManager . instance ( ) . getGatewayRegistryEntry ( ) ; outputAdapter . log ( LogType . ERROR , `${ gatewayRegistryEntry . name } has no instantiated chaincodes` ) ; return ; } const quickPickItems = [ ] ; for ( const chaincode of instantiatedChaincodes ) { const data = { name : chaincode . name , channel : chaincode . channel , version : chaincode . version } ; quickPickItems . push ( { label : `${ chaincode . name }@${ chaincode . version }` , data : data } ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showRuntimeInstantiatedSmartContractsQuickPick ( prompt , channelName ? ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } const channelMap = await connection . createChannelMap ( ) ; const instantiatedChaincodes = [ ] ; for ( const [ thisChannelName , peerNames ] of channelMap ) { if ( channelName && ( channelName !== thisChannelName ) ) { continue ; } const chaincodes = await connection . getCommittedSmartContractDefinitions ( peerNames , thisChannelName ) ; for ( const chaincode of chaincodes ) { const data = { name : chaincode . name , version : chaincode . version , channel : thisChannelName } ; instantiatedChaincodes . push ( data ) ; } } if ( instantiatedChaincodes . length === 0 ) { const environmentName = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) . name ; outputAdapter . log ( LogType . ERROR , `${ environmentName } has no instantiated chaincodes` ) ; return ; } const quickPickItems = [ ] ; for ( const chaincode of instantiatedChaincodes ) { const data = { name : chaincode . name , channel : chaincode . channel , version : chaincode . version } ; quickPickItems . push ( { label : `${ chaincode . name }@${ chaincode . version }` , data : data } ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showTestFileOverwriteQuickPick ( prompt ) { const options = [ this . YES , this . NO , this . GENERATE_NEW_TEST_FILE ] ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( options , quickPickOptions ) ; } public static async showContractQuickPick ( prompt , contracts ) { const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( contracts , quickPickOptions ) ; } public static async showTransactionQuickPick ( prompt , chaincodeName , channelName ) { const fabricConnectionManager = FabricGatewayConnectionManager . instance ( ) ; const connection = fabricConnectionManager . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , 'No connection to a blockchain found' ) ; return ; } const quickPickItems = [ ] ; let transactionNamesMap ; await vscode . window . withProgress ( { location : vscode . ProgressLocation . Notification , title : 'IBM Blockchain Platform Extension' , cancellable : false } , async ( progress ) => { progress . report ( { message : 'Getting transaction information' } ) ; transactionNamesMap = await MetadataUtil . getTransactionNames ( connection , chaincodeName , channelName ) ; } ) ; if ( ! transactionNamesMap ) { const transactionName = await UserInputUtil . showInputBox ( 'What function do you want to call?' ) ; if ( ! transactionName ) { return ; } return { label : null , data : { name : transactionName , contract : null } } ; } for ( const [ name , transactionArray ] of transactionNamesMap ) { for ( const transaction of transactionArray ) { const data = { name : transaction , contract : name } ; if ( name !== '' ) { quickPickItems . push ( { label : `${ name } - ${ transaction }` , data : data } ) ; } else { quickPickItems . push ( { label : `${ transaction }` , data : data } ) ; } } } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showInstallableSmartContractsQuickPick ( prompt , peers ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } const quickPickItems = [ ] ; let packagedContracts = await this . getPackagedContracts ( ) ; for ( const peer of peers ) { const chaincodes = await connection . getInstalledSmartContracts ( peer , true ) ; chaincodes . forEach ( ( chaincode ) => { packagedContracts = packagedContracts . filter ( ( _package ) => { return chaincode . label !== _package . label && _package . data . packageEntry . path . endsWith ( '.cds' ) ; } ) ; } ) ; } quickPickItems . push ( ... packagedContracts ) ; const openProjects = await this . getOpenProjects ( ) ; for ( const openProject of openProjects ) { const workspace = openProject . data . workspace ; const data = { packageEntry : undefined , workspace : workspace } ; quickPickItems . push ( { label : `${ openProject . data . workspace . name }` , description : openProject . description , data : data } ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; if ( quickPickItems . length === 0 ) { throw new Error ( 'No packages found to install' ) ; } else { return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } } public static async openNewProject ( openMethod , uri , workspaceLabel ? ) { if ( openMethod === UserInputUtil . ADD_TO_WORKSPACE ) { const openFolders = vscode . workspace . workspaceFolders || [ ] ; const options = ( workspaceLabel ) ? { uri : uri , name : workspaceLabel } : { uri : uri } ; vscode . workspace . updateWorkspaceFolders ( openFolders . length , 0 , options ) ; } else { let openNewWindow = true ; if ( openMethod === UserInputUtil . OPEN_IN_CURRENT_WINDOW ) { openNewWindow = false ; await this . checkForUnsavedFiles ( ) ; } await vscode . commands . executeCommand ( 'vscode.openFolder' , uri , openNewWindow ) ; } } public static async showAddWalletOptionsQuickPick ( prompt , addIdentity ) { const addIdentityOptions = [ this . IMPORT_WALLET ] ; if ( addIdentity ) { addIdentityOptions . push ( this . WALLET_NEW_ID ) ; } else { addIdentityOptions . push ( this . WALLET ) ; } const quickPickOptions = { matchOnDetail : true , placeHolder : prompt , ignoreFocusOut : true , canPickMany : false , } ; return vscode . window . showQuickPick ( addIdentityOptions , quickPickOptions ) ; } public static async browse ( placeHolder , browseOption , openDialogOptions , returnUri ? ) { const result = await vscode . window . showQuickPick ( [ browseOption as any ] , { placeHolder } ) ; if ( ! result ) { return ; } else { return this . openFileBrowser ( openDialogOptions , returnUri ) ; } } public static async browseWithOptions ( placeHolder , quickPickItems , openDialogOptions , returnUri ? ) { const result = await vscode . window . showQuickPick ( quickPickItems as any [ ] , { placeHolder } ) ; if ( ! result ) { return ; } else if ( ( typeof result === 'string' ) ) { return ( result !== this . BROWSE_LABEL ) ? result : this . openFileBrowser ( openDialogOptions , returnUri ) ; } else { return ( result . label !== this . BROWSE_LABEL ) ? result : this . openFileBrowser ( openDialogOptions , returnUri ) ; } } public static async openFileBrowser ( openDialogOptions , returnUri ? ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; try { await TimerUtil . sleep ( 500 ) ; const fileBrowser = await vscode . window . showOpenDialog ( openDialogOptions ) ; if ( ! fileBrowser ) { return ; } if ( returnUri ) { if ( openDialogOptions . canSelectMany ) { return fileBrowser ; } else { return fileBrowser [ 0 ] ; } } else { return fileBrowser [ 0 ] . fsPath ; } } catch ( error ) { outputAdapter . log ( LogType . ERROR , error . message , error . toString ( ) ) ; } } public static async showCertificateAuthorityQuickPickBox ( prompt ) { const connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( ! connection ) { VSCodeBlockchainOutputAdapter . instance ( ) . log ( LogType . ERROR , undefined , 'No connection to a blockchain found' ) ; return ; } const caNames = connection . getAllCertificateAuthorityNames ( ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; if ( caNames . length > 1 ) { return vscode . window . showQuickPick ( caNames , quickPickOptions ) ; } else { return caNames [ 0 ] ; } } public static async showWalletsQuickPickBox ( prompt , canPickMany , showLocalWallet ? , showCreateWallet ? ) { const walletQuickPickItems = [ ] ; const wallets = await FabricWalletRegistry . instance ( ) . getAll ( showLocalWallet ) ; for ( const walletRegistryEntry of wallets ) { walletQuickPickItems . push ( { label : ( walletRegistryEntry . displayName ) ? walletRegistryEntry . displayName : walletRegistryEntry . name , data : walletRegistryEntry } ) ; } if ( showCreateWallet ) { walletQuickPickItems . push ( { label : '+ Add new wallet' , data : undefined } ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; return vscode . window . showQuickPick ( walletQuickPickItems , quickPickOptions ) ; } public static async addIdentityMethod ( isLocalWallet ) { const options = [ UserInputUtil . ADD_CERT_KEY_OPTION , UserInputUtil . ADD_JSON_ID_OPTION ] ; if ( isLocalWallet ) { options . push ( UserInputUtil . ADD_LOCAL_ID_SECRET_OPTION ) ; } else { options . push ( UserInputUtil . ADD_ID_SECRET_OPTION ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : UserInputUtil . ADD_IDENTITY_METHOD } ; return vscode . window . showQuickPick ( options , quickPickOptions ) ; } public static async getCertKey ( ) { const openDialogOptions = { canSelectFiles : true , canSelectFolders : false , canSelectMany : false , openLabel : 'Select' , filters : undefined } ; const certificatePath = await UserInputUtil . browse ( 'Browse for a certificate file' , UserInputUtil . BROWSE_LABEL , openDialogOptions ) as string ; if ( ! certificatePath ) { return ; } const certificate = FabricCertificate . loadFileFromDisk ( certificatePath ) ; FabricCertificate . validateCertificate ( certificate ) ; const privateKeyPath = await UserInputUtil . browse ( 'Browse for a private key file' , UserInputUtil . BROWSE_LABEL , openDialogOptions ) as string ; if ( ! privateKeyPath ) { return ; } const privateKey = FabricCertificate . loadFileFromDisk ( privateKeyPath ) ; FabricCertificate . validatePrivateKey ( privateKey ) ; return { certificatePath , privateKeyPath } ; } public static async getEnrollIdSecret ( ) { const enrollmentID = await UserInputUtil . showInputBox ( 'Enter enrollment ID' ) ; if ( ! enrollmentID ) { return ; } const enrollmentSecret = await UserInputUtil . showInputBox ( 'Enter enrollment secret' ) ; if ( ! enrollmentSecret ) { return ; } return { enrollmentID , enrollmentSecret } ; } public static async showDebugCommandList ( commands , prompt ) { const quickPickItems = [ ] ; for ( const command of commands ) { quickPickItems . push ( { label : command . name , data : command . command } ) ; } const quickPickOptions = { ignoreFocusOut : true , canPickMany : false , placeHolder : prompt } ; return vscode . window . showQuickPick ( quickPickItems , quickPickOptions ) ; } public static async showNodesInEnvironmentQuickPick ( prompt , environmentRegistryEntry , nodeTypefilter , showAsociatedIdentity = false , canPickMany = false , showUnassociatedNodes = false ) { const environment = EnvironmentFactory . getEnvironment ( environmentRegistryEntry ) ; let nodes = await environment . getNodes ( showUnassociatedNodes ) ; if ( nodeTypefilter . length > 0 ) { nodes = nodes . filter ( ( node ) => nodeTypefilter . indexOf ( node . type ) !== - 1 ) ; } const quickPickItems = UserInputUtil . selectNodesOneOrdererPerCluster ( nodes , showAsociatedIdentity ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; if ( quickPickItems . length === 1 ) { if ( canPickMany ) { return quickPickItems ; } else { return quickPickItems [ 0 ] ; } } else if ( quickPickItems . length === 0 ) { throw new Error ( 'No nodes found to choose from' ) ; } return vscode . window . showQuickPick < IBlockchainQuickPickItem < FabricNode > > ( quickPickItems , quickPickOptions ) ; } public static async failedActivationWindow ( error ) { const statusPagePrompt = 'Check status website' ; const retryPrompt = 'Retry activation' ; const response = await vscode . window . showErrorMessage ( `Failed to activate extension: ${ error }` , statusPagePrompt , retryPrompt ) ; if ( response === retryPrompt ) { await vscode . commands . executeCommand ( 'workbench.action.reloadWindow' ) ; } else if ( response === statusPagePrompt ) { const uri = vscode . Uri . parse ( 'https://ibm-blockchain.github.io/blockchain-vscode-extension/' ) ; await vscode . commands . executeCommand ( 'vscode.open' , uri ) ; } } public static async failedNetworkStart ( popupMsg , logMsg ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; outputAdapter . log ( LogType . ERROR , undefined , logMsg ) ; const response = await vscode . window . showErrorMessage ( popupMsg , UserInputUtil . MORE_DETAILS ) ; if ( response === UserInputUtil . MORE_DETAILS ) { outputAdapter . show ( ) ; } } returns public static async chooseWorkspace ( isPackaging ) { let workspaceFolderOptions ; let workspaceFolder ; const text = isPackaging ? 'package' : 'create functional tests for' ; workspaceFolderOptions = UserInputUtil . getWorkspaceFolders ( ) ; if ( workspaceFolderOptions . length === 0 ) { const message = `Issue determining available smart contracts. Please open the smart contract you want to ${ text }.` ; throw new Error ( message ) ; } const minOpenToPick = isPackaging ? 2 : 1 ; if ( workspaceFolderOptions . length >= minOpenToPick ) { const chosenFolder = await UserInputUtil . showWorkspaceQuickPickBox ( `Choose a workspace folder to ${ text }` ) ; if ( ! chosenFolder ) { return ; } workspaceFolder = chosenFolder . data ; } else { workspaceFolder = workspaceFolderOptions [ 0 ] ; } return workspaceFolder ; } param workspaceDir { String } returns { string } public static async getLanguage ( workspaceDir ) { const packageJsonFile = path . join ( workspaceDir . uri . fsPath , 'package.json' ) ; const packageJsonFileExists = await fs . pathExists ( packageJsonFile ) ; if ( packageJsonFileExists ) { return 'node' ; } const gradleFile = path . join ( workspaceDir . uri . fsPath , 'build.gradle' ) ; const gradleFileExists = await fs . pathExists ( gradleFile ) ; const mavenFile = path . join ( workspaceDir . uri . fsPath , 'pom.xml' ) ; const mavenFileExists = await fs . pathExists ( mavenFile ) ; if ( gradleFileExists || mavenFileExists ) { return 'java' ; } const goFiles = await vscode . workspace . findFiles ( new vscode . RelativePattern ( workspaceDir , '**/*.go' ) , null , 1 ) ; if ( goFiles . length > 0 ) { return 'golang' ; } const message = `Failed to determine workspace language type, supported languages are JavaScript, TypeScript, Go and Java. Please ensure your contract's root-level directory is open in the Explorer.` ; throw new Error ( message ) ; } public static async showChannelPeersQuickPick ( channelPeers ) { const quickPickItems = [ ] ; for ( const peer of channelPeers ) { quickPickItems . push ( { label : peer . name , description : peer . mspID , data : peer . name } ) ; } return vscode . window . showQuickPick ( quickPickItems , { ignoreFocusOut : true , canPickMany : true , placeHolder : `Select the peers to send the transaction to` } ) ; } public static async showNodesQuickPickBox ( prompt , nodes , canPickMany , currentNodes ? , informOfChanges = false ) { if ( nodes . length === 0 ) { throw new Error ( 'Error when importing nodes, no nodes found to choose from.' ) ; } let changeDetected = false ; const quickPickItems = UserInputUtil . selectNodesOneOrdererPerCluster ( nodes ) ; if ( currentNodes && currentNodes . length > 0 ) { quickPickItems . map ( ( quickPickItem ) => { const existingNode = currentNodes . find ( ( node ) => node . api_url === quickPickItem . data . api_url ) ; if ( existingNode ) { if ( existingNode . hidden === undefined || existingNode . hidden === false ) { quickPickItem . picked = true ; } if ( existingNode . name !== quickPickItem . data . name ) { quickPickItem . description = `(was ${ existingNode . name })` ; changeDetected = true ; } } else { quickPickItem . description = '(new)' ; changeDetected = true ; } return quickPickItem ; } ) ; } if ( informOfChanges ) { if ( changeDetected ) { FabricEnvironmentManager . instance ( ) . stopEnvironmentRefresh ( ) ; let editFilters ; if ( ExtensionUtil . isChe ( ) ) { editFilters = true ; } else { editFilters = await UserInputUtil . showConfirmationWarningMessage ( 'Differences have been detected between the local environment and the Ops Tools environment. Would you like to filter nodes?' ) ; } if ( ! editFilters ) { return ; } } else { return ; } } const sortedQuickPickItems = UserInputUtil . sortNodesForQuickpick ( quickPickItems ) ; const quickPickOptions = { ignoreFocusOut : true , canPickMany : canPickMany , placeHolder : prompt } ; if ( ExtensionUtil . isChe ( ) ) { return sortedQuickPickItems . map ( ( sortedQuickPickItem ) => { sortedQuickPickItem . picked = true ; return sortedQuickPickItem ; } ) ; } return vscode . window . showQuickPick ( sortedQuickPickItems , quickPickOptions ) ; } public static sortNodesForQuickpick ( items ) { const alphabeticalSortFn = ( a , b ) : number => a . label . localeCompare ( b . label ) ; const newNodes = items . filter ( ( item ) => item . description && item . description === '(new)' ) . sort ( alphabeticalSortFn ) ; const pickedNodes = items . filter ( ( item ) => item . picked && item . picked === true ) . sort ( alphabeticalSortFn ) ; const hiddenNodes = items . filter ( ( item ) => ! item . picked && ( ! item . description || item . description !== '(new)' ) ) . sort ( alphabeticalSortFn ) ; return newNodes . concat ( hiddenNodes , pickedNodes ) ; } public static async showExternalLinkButton ( message , callToAction , url ) { const selectedText = await vscode . window . showInformationMessage ( message , callToAction ) ; if ( selectedText === callToAction ) { vscode . env . openExternal ( vscode . Uri . parse ( url ) ) ; } } public static async selectCapabilities ( prompt ) { const result = await UserInputUtil . showQuickPickItem ( prompt , [ { label : UserInputUtil . V2_0_RECOMMENDED , data : UserInputUtil . V2_0 } , { label : UserInputUtil . V1_4_2 , data : UserInputUtil . V1_4_2 } ] , false ) as IBlockchainQuickPickItem < string > ; if ( result ) { return result . data ; } } private static async checkForUnsavedFiles ( ) { const unsavedFiles = vscode . workspace . textDocuments . find ( ( document ) => { return document . isDirty ; } ) ; if ( unsavedFiles ) { const answer = await UserInputUtil . showQuickPickYesNo ( 'Do you want to save any unsaved changes?' ) ; if ( answer === UserInputUtil . YES ) { await vscode . workspace . saveAll ( true ) ; } } } private static async getInstalledContracts ( connection , peers , isV1 = false ) { const tempQuickPickItems = [ ] ; for ( const peer of peers ) { const chaincodes = await connection . getInstalledSmartContracts ( peer , isV1 ) ; chaincodes . forEach ( ( chaincode ) => { const nameAndVersion = chaincode . label . split ( '@' ) ; const _package = new PackageRegistryEntry ( { name : nameAndVersion [ 0 ] , version : nameAndVersion [ 1 ] , path : undefined , sizeKB : undefined } ) ; const data = { packageEntry : _package , workspace : undefined } ; const label = chaincode . label ; const foundItem = tempQuickPickItems . find ( ( item ) => { return item . label === label ; } ) ; if ( ! foundItem ) { tempQuickPickItems . push ( { label : label , description : 'Installed' , data : data } ) ; } } ) ; } return tempQuickPickItems ; } private static async getPackagedContracts ( ) { const tempQuickPickItems = [ ] ; const packages = await PackageRegistry . instance ( ) . getAll ( ) ; for ( const _package of packages ) { const data = { packageEntry : _package , workspace : undefined } ; const label = `${ _package . name }@${ _package . version }` ; tempQuickPickItems . push ( { label : label , description : 'Packaged' , data : data } ) ; } return tempQuickPickItems ; } private static async getOpenProjects ( ) { const tempQuickPickItems = [ ] ; const workspaceFolderOptions = UserInputUtil . getWorkspaceFolders ( ) ; for ( const workspace of workspaceFolderOptions ) { const data = { packageEntry : undefined , workspace : workspace } ; const label = `${ workspace . name }` ; tempQuickPickItems . push ( { label : label , description : 'Open Project' , data : data } ) ; } return tempQuickPickItems ; } private static selectNodesOneOrdererPerCluster ( nodes , showAsociatedIdentity = false ) { const quickPickItems = [ ] ; for ( const _node of nodes ) { if ( _node . type === FabricNodeType . ORDERER && _node . cluster_name ) { const foundItem = quickPickItems . find ( ( item ) => item . data . cluster_name === _node . cluster_name ) ; if ( ! foundItem ) { const quickPickItem = { label : _node . cluster_name , data : _node } ; if ( showAsociatedIdentity && _node . wallet && _node . identity ) { quickPickItem . description = `Associated with identity: ${ _node . identity } in wallet: ${ _node . wallet }` ; } quickPickItems . push ( quickPickItem ) ; } } else { const quickPickItem = { label : _node . name , data : _node } ; if ( showAsociatedIdentity && _node . wallet && _node . identity ) { quickPickItem . description = `Associated with identity: ${ _node . identity } in wallet: ${ _node . wallet }` ; } quickPickItems . push ( quickPickItem ) ; } } return quickPickItems ; } }