This commit is contained in:
Cale Teeter 2020-05-04 08:54:07 -04:00
Родитель a1ed777db3
Коммит 430b073c9d
53 изменённых файлов: 7195 добавлений и 31 удалений

15
.vscode/launch.json поставляемый
Просмотреть файл

@ -51,6 +51,21 @@
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/out/src/debugger.js",
"args": [ "--server=4711" ]
},
{
"type": "node",
"request": "launch",
"name": "Debug Ui tests",
"program": "${workspaceFolder}/ui-test/node_modules/vscode-extension-tester/out/cli.js",
"args": [
"run-tests",
"ui-test/out/*.test.js",
"-s",
"ui-test/test-resources"
],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/ui-test/out/*/*.js"],
"preLaunchTask": "Prepare resources for ui-tests"
}
],
"compounds": [

12
.vscode/tasks.json поставляемый
Просмотреть файл

@ -68,6 +68,18 @@
"Build Drizzle UI",
"Build extension"
]
},
{
"label": "Prepare resources for ui-tests",
"type": "npm",
"script": "prepare-ui-test",
"path": "ui-test/",
"presentation": {
"reveal": "silent",
"panel": "shared",
"showReuseMessage": false
},
"group": "test"
}
]
}

Просмотреть файл

@ -13,6 +13,7 @@ out/**
!out/src/checkTruffleConfigTemplate.js
src/**
test/**
ui-test/**
.editorconfig
.gitignore
.npmignore

24
.vscodeignore.orig Normal file
Просмотреть файл

@ -0,0 +1,24 @@
.idea/**
.vscode/**
.vscode-test/**
coverage/**
drizzleUI/**
node_modules/**
out/**
!out/src/mscorlib.js
!out/src/Nethereum.Generators.DuoCode.js
!out/src/extension.js
!out/src/debugger.js
!out/src/web3ProviderResolver.js
!out/src/checkTruffleConfigTemplate.js
src/**
test/**
.editorconfig
.gitignore
.npmignore
**/tsconfig.json
**/tslint.json
**/*.map
**/*.ts
**/webpack.config.js
**/coverconfig.json

Просмотреть файл

@ -2,6 +2,24 @@
All notable changes to the "azure blockchain" extension will be documented in this file.
## 1.3.0
### Enhancements
- Add version 2.5 of OpenZeppelin contracts to the extension
- Added new code generator for logic apps
- Added new code generator for flow apps
- Adding initial code for decoupling frameworks from UI to support partner extensions better
### Fixes
- Fixed loader error for typescript in webpack (packaging error)
- Fixed some unit test issues
### Internal Improvements
- Improving package locks for specific core packages to help with stability
## 1.2.0
### Enhancements

151
CHANGELOG.md.orig Normal file
Просмотреть файл

@ -0,0 +1,151 @@
# Change Log
All notable changes to the "azure blockchain" extension will be documented in this file.
## 1.2.0
### Enhancements
- Add the ability to create new Blockchain Applications for Blockchain Data Manager
- Remove token menu and UI from extension
- Add changelog popup on extension upgrade
### Fixes
- Fix issue with broken code generation when using custom build directories
- Fix issue with OpenZeppelin contracts not showing as valid contracts for Blockchain Data Manager applications
### Internal Improvements
- Updates to telemetry for ABS deployments
## 1.1.0
### Enhancements
- Implement BDM core operations
### Fixes
- Fixed error when adding Mocks category from OpenZeppelin
- Removed unnecessary notifications when deploying to removed networks
### Internal Improvements
## 1.0.0
### Enhancements
- Added UI to handle required parameters for OpenZeppelin contract deployment.
- Added Blockchain Data Manager to the core view for connecting to existing instances.
### Fixes
- Cleanup of obfuscation for build directory path.
- Merge of public PR for url checker to include basic auth.
### Internal Improvements
- Refactoring custom build directory code.
## 0.1.13
### Enhancements
- Added progress notifications for long running tasks.
- Updated transaction bytecode copy option to support libraries and complex contracts.
- Added the ability to upgrade contracts deployed as part of OpenZeppelin.
- Added the ability for contract build redirects from configuration to be respected.
### Fixes
- Updated requirements for minimum VS Code version to 1.36.1. This was to resolve some issues specific to Mac OS.
- Updated notifications for code generation to be accurate for each type (logic app, flow app, function).
### Internal Improvements
- Test runner enhancements to fix some test issues (timing)
- Updated search tags for SEO
## 0.1.12
- Deployments from external truffle boxes can now deploy to Azure Blockchain Service
- Additional context menu for deployed contract bytecode (transaction bytecode)
- Fix on Mocks category for OpenZeppelin to remove the dead link for not existent docs
- Better handling of OpenZeppelin when multiple categories are downloaded.
- Fix misspelling of OpenZeppelin on the welcome page.
## 0.1.11
- Fixed issue with Logic App generation JSON schema
- Added Contract UI support for contracts deployed on Azure Blockchain Service
- Updated deprecated Truffle NPM packages
- Bumped HD wallet provider to latest Truffle version
- Minimized output channel logs
- Fixed output channel issue which shows JavaScript object after contract migration
- Preview features added for token (TTI compliant) generation were added.
## 0.1.10
- Fixed Drizzle error handling issues
- Updated Azure Blockchain Service logos in tree view
- Updated Azure Blockchain Development Kit logo
- Fixed unit tests for debugger, OpenZeppelin and Truffle commands
- Improved sign-in support for Infura projects
## 0.1.9
- Added support for Infura project integration and account management
## 0.1.8
- Added support for Open Zeppelin contract/library download and deployment
- Contract UI/Interaction functionality updates
- Implement support for array types as function parameters
- Support Enum types
- Bug fixes
- Ganache not properly shutting down on VS Code exit
- Closing open items raised in the extension VS Code public GitHub repo
## 0.1.7
- Added contract UI/interaction feature
- Added Solidity debugging feature
## 0.1.6
- Added telemetry reporting capabilities
## 0.1.5
- Backend test coverage
- Cleanup of packaging output, optimization
- Truffle installation fails on new installs fix
- Improved support for multiple ganache instances
- Preflight validation for ABS deployments
- Cleanup of code generation output
- Ganache commands cleanup
- Add command to export private key from mnemonic
- Better error handling ABS deployments
## 0.1.4
- bug fixes
## 0.1.3
- various bug fixes
- moved logic app/function/flow generators out of Truffle build directory into their own directory
- refactoring of Welcome/Requirements pages
## 0.1.2
- doc and bug fixes for contract code generation
## 0.1.1
- Updated menu options
## 0.1.0
- Initial release

Просмотреть файл

@ -5,7 +5,7 @@
"publisher": "AzBlockchain",
"preview": false,
"icon": "images/blockchain-service-logo.png",
"version": "1.2.0",
"version": "1.3.0",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum"
@ -41,8 +41,8 @@
"onCommand:contract.copyABI",
"onCommand:truffle.createLocalUI",
"onCommand:truffle.newSolidityProject",
"onCommand:truffle.buildContracts",
"onCommand:truffle.deployContracts",
"onCommand:azureBlockchainService.buildContracts",
"onCommand:azureBlockchainService.deployContracts",
"onCommand:azureBlockchainService.generateReportPublishingWorkflows",
"onCommand:azureBlockchainService.generateMicroservicesWorkflows",
"onCommand:azureBlockchainService.generateEventPublishingWorkflows",
@ -72,12 +72,21 @@
},
"azureBlockchainService.openZeppelin.version": {
"type": "string",
"default": "2.4.0",
"default": "2.5.0",
"scope": "The latest Open Zeppelin version"
},
"azureBlockchainService.storageAccount.name": {
"type": "string",
"scope": "Storage Account name"
},
"azureBlockchainService.coreSDK": {
"type": "string",
"scope": "Core SDK for extensions backend",
"default": "Truffle",
"enum": [
"Truffle",
"OpenZeppelin"
]
}
}
},
@ -137,12 +146,12 @@
"category": "Azure Blockchain"
},
{
"command": "truffle.buildContracts",
"command": "azureBlockchainService.buildContracts",
"title": "Build Contracts",
"category": "Azure Blockchain"
},
{
"command": "truffle.deployContracts",
"command": "azureBlockchainService.deployContracts",
"title": "Deploy Contracts",
"category": "Azure Blockchain"
},
@ -278,11 +287,11 @@
},
{
"when": "azureBlockchainService:isWorkspaceOpen",
"command": "truffle.buildContracts"
"command": "azureBlockchainService.buildContracts"
},
{
"when": "azureBlockchainService:isWorkspaceOpen",
"command": "truffle.deployContracts"
"command": "azureBlockchainService.deployContracts"
},
{
"when": "false",
@ -380,7 +389,7 @@
"explorer/context": [
{
"when": "resourceLangId == solidity",
"command": "truffle.buildContracts",
"command": "azureBlockchainService.buildContracts",
"group": "8_buildContractGroup"
},
{
@ -390,12 +399,12 @@
},
{
"when": "resourceLangId == json",
"command": "truffle.deployContracts",
"command": "azureBlockchainService.deployContracts",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "truffle.deployContracts",
"command": "azureBlockchainService.deployContracts",
"group": "8_buildContractGroup"
},
{
@ -559,7 +568,7 @@
"@types/sinon": "^7.0.11",
"@types/uuid": "^3.4.4",
"@types/vscode": "^1.36.1",
"copy-webpack-plugin": "^5.0.3",
"copy-webpack-plugin": "5.1.1",
"decache": "^4.5.1",
"glob": "^7.1.4",
"husky": "^2.4.0",
@ -570,15 +579,15 @@
"rewire": "^4.0.1",
"sinon": "^7.3.2",
"truffle": "^5.0.14",
"ts-loader": "6.2.1",
"ts-loader": "7.0.2",
"tslint": "^5.18.0",
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.3.1",
"typescript": "3.8.3",
"vscode-debugadapter": "^1.33.0",
"vscode-debugprotocol": "^1.33.0",
"vscode-test": "^1.2.0",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.6"
"webpack": "4.43.0",
"webpack-cli": "3.3.11"
},
"dependencies": {
"@truffle/debug-utils": "1.0.19",
@ -600,8 +609,9 @@
"request-promise": "^4.2.4",
"semver": "^6.0.0",
"uuid": "^3.3.2",
"vscode-azureextensionui": "^0.25.1",
"vscode-extension-telemetry": "^0.1.1"
"vscode-azureextensionui": "^0.29.12",
"vscode-extension-telemetry": "^0.1.1",
"solidity-parser-antlr": "^0.4.11"
},
"extensionDependencies": [
"JuanBlanco.solidity",
@ -618,4 +628,4 @@
"git add"
]
}
}
}

621
package.json.orig Normal file
Просмотреть файл

@ -0,0 +1,621 @@
{
"name": "azure-blockchain",
"displayName": "Azure Blockchain Development Kit for Ethereum",
"description": "Develop, deploy debug and manage your Azure Blockchain Service solution",
"publisher": "AzBlockchain",
"preview": false,
"icon": "images/blockchain-service-logo.png",
"version": "1.2.0",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum"
},
"bugs": {
"url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum/issues"
},
"homepage": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum/blob/master/README.md",
"license": "MIT",
"engines": {
"vscode": "^1.36.1"
},
"keywords": [
"blockchain",
"azure",
"ethereum",
"debuggers",
"snippet"
],
"categories": [
"Azure",
"Extension Packs",
"Programming Languages"
],
"aiKey": "INSERTAIKEY",
"main": "./out/src/extension.js",
"activationEvents": [
"onView:AzureBlockchain",
"onCommand:azureBlockchainService.showWelcomePage",
"onCommand:azureBlockchainService.refresh",
"onCommand:contract.copyByteCode",
"onCommand:contract.copyDeployedByteCode",
"onCommand:contract.copyABI",
"onCommand:truffle.createLocalUI",
"onCommand:truffle.newSolidityProject",
"onCommand:truffle.buildContracts",
"onCommand:truffle.deployContracts",
"onCommand:azureBlockchainService.generateReportPublishingWorkflows",
"onCommand:azureBlockchainService.generateMicroservicesWorkflows",
"onCommand:azureBlockchainService.generateEventPublishingWorkflows",
"onCommand:azureBlockchainService.generateDataPublishingWorkflows",
"onCommand:azureBlockchainService.connectProject",
"onCommand:azureBlockchainService.showSmartContractPage",
"onCommand:azureBlockchainService.copyRPCEndpointAddress",
"onCommand:azureBlockchainService.createProject",
"onCommand:azureBlockchainService.disconnectProject",
"onCommand:extension.truffle.debugTransaction",
"onCommand:openZeppelin.addCategory",
"onCommand:azureBlockchainService.signInToInfuraAccount",
"onCommand:azureBlockchainService.signOutOfInfuraAccount",
"onCommand:azureBlockchainService.openAtAzurePortal",
"onCommand:azureBlockchainService.createNewBDMApplication",
"onCommand:azureBlockchainService.deleteBDMApplication",
"onDebug"
],
"contributes": {
"configuration": {
"title": "Azure Blockchain Development Kit for Ethereum",
"properties": {
"azureBlockchainService.ignoreLongRunningTaskNotification": {
"type": "boolean",
"default": false,
"description": "Disable notification on long running tasks."
},
"azureBlockchainService.openZeppelin.version": {
"type": "string",
"default": "2.4.0",
"scope": "The latest Open Zeppelin version"
},
"azureBlockchainService.storageAccount.name": {
"type": "string",
"scope": "Storage Account name"
}
}
},
"commands": [
{
"command": "azureBlockchainService.generateMicroservicesWorkflows",
"title": "Generate Microservices for Smart Contracts",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.generateDataPublishingWorkflows",
"title": "Generate Data Publishing Workflows for Smart Contracts",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.generateReportPublishingWorkflows",
"title": "Generate Report Publishing Workflows for Smart Contracts",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.generateEventPublishingWorkflows",
"title": "Generate Event Publishing Workflows for Smart Contracts",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.showWelcomePage",
"title": "Show Welcome Page",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.refresh",
"title": "Refresh",
"category": "Azure Blockchain",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "contract.copyByteCode",
"title": "Copy Constructor Bytecode",
"category": "Azure Blockchain"
},
{
"command": "contract.copyDeployedByteCode",
"title": "Copy Transaction Bytecode",
"category": "Azure Blockchain"
},
{
"command": "contract.copyABI",
"title": "Copy Contract ABI",
"category": "Azure Blockchain"
},
{
"command": "truffle.newSolidityProject",
"title": "New Solidity Project",
"category": "Azure Blockchain"
},
{
"command": "truffle.buildContracts",
"title": "Build Contracts",
"category": "Azure Blockchain"
},
{
"command": "truffle.deployContracts",
"title": "Deploy Contracts",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.connectProject",
"title": "Connect to network",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.showSmartContractPage",
"title": "Show Smart Contract Interaction Page",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.copyRPCEndpointAddress",
"title": "Copy RPC Endpoint Address",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.createProject",
"title": "Create a new network",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.disconnectProject",
"title": "Disconnect",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.startGanacheServer",
"title": "Start Ganache Server",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.stopGanacheServer",
"title": "Stop Ganache Server",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.signInToInfuraAccount",
"title": "Sign in to Infura account",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.signOutOfInfuraAccount",
"title": "Sign out of Infura account",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.getPrivateKey",
"title": "Retrieve private key",
"category": "Azure Blockchain"
},
{
"command": "extension.truffle.debugTransaction",
"title": "Debug Transaction",
"category": "Azure Blockchain"
},
{
"command": "openZeppelin.addCategory",
"title": "Add contracts from OpenZeppelin",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.openAtAzurePortal",
"title": "Open at Azure Portal",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.createNewBDMApplication",
"title": "Create Blockchain Application",
"category": "Azure Blockchain"
},
{
"command": "azureBlockchainService.deleteBDMApplication",
"title": "Delete Blockchain Application",
"category": "Azure Blockchain"
}
],
"breakpoints": [
{
"language": "solidity"
},
{
"language": "sol"
}
],
"menus": {
"commandPalette": [
{
"when": "false",
"command": "azureBlockchainService.showSmartContractPage"
},
{
"when": "false",
"command": "azureBlockchainService.refresh"
},
{
"when": "false",
"command": "azureBlockchainService.copyRPCEndpointAddress"
},
{
"when": "false",
"command": "azureBlockchainService.disconnectProject"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateMicroservicesWorkflows"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateDataPublishingWorkflows"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateEventPublishingWorkflows"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateReportPublishingWorkflows"
},
{
"when": "false",
"command": "contract.copyByteCode"
},
{
"when": "false",
"command": "contract.copyDeployedByteCode"
},
{
"when": "false",
"command": "contract.copyABI"
},
{
"when": "azureBlockchainService:isWorkspaceOpen",
"command": "truffle.buildContracts"
},
{
"when": "azureBlockchainService:isWorkspaceOpen",
"command": "truffle.deployContracts"
},
{
"when": "false",
"command": "openZeppelin.addCategory"
},
{
"when": "false",
"command": "azureBlockchainService.openAtAzurePortal"
},
{
"when": "false",
"command": "azureBlockchainService.deleteBDMApplication"
},
{
"when": "false",
"command": "azureBlockchainService.createNewBDMApplication"
}
],
"view/title": [
{
"command": "azureBlockchainService.showWelcomePage",
"when": "view == AzureBlockchain"
},
{
"command": "azureBlockchainService.createProject",
"when": "view == AzureBlockchain"
},
{
"command": "azureBlockchainService.connectProject",
"when": "view == AzureBlockchain"
},
{
"command": "azureBlockchainService.refresh",
"when": "view == AzureBlockchain",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "azureBlockchainService.disconnectProject",
"when": "view == AzureBlockchain && viewItem == project",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.startGanacheServer",
"when": "view == AzureBlockchain && viewItem == localproject",
"group": "azureBlockchain-1@0"
},
{
"command": "azureBlockchainService.stopGanacheServer",
"when": "view == AzureBlockchain && viewItem == localproject",
"group": "azureBlockchain-1@1"
},
{
"command": "azureBlockchainService.disconnectProject",
"when": "view == AzureBlockchain && viewItem == localproject",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.disconnectProject",
"when": "view == AzureBlockchain && viewItem == bdmproject",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.copyRPCEndpointAddress",
"when": "view == AzureBlockchain && viewItem == network",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.copyRPCEndpointAddress",
"when": "view == AzureBlockchain && viewItem == localnetwork",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.openAtAzurePortal",
"when": "view == AzureBlockchain && viewItem == input",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.openAtAzurePortal",
"when": "view == AzureBlockchain && viewItem == output",
"group": "azureBlockchain-0@0"
},
{
"command": "azureBlockchainService.createNewBDMApplication",
"when": "view == AzureBlockchain && viewItem == bdmproject",
"group": "azureBlockchain-1@1"
},
{
"command": "azureBlockchainService.deleteBDMApplication",
"when": "view == AzureBlockchain && viewItem == bdmApplication",
"group": "azureBlockchain-0@0"
}
],
"explorer/context": [
{
"when": "resourceLangId == solidity",
"command": "truffle.buildContracts",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.showSmartContractPage",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == json",
"command": "truffle.deployContracts",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "truffle.deployContracts",
"group": "8_buildContractGroup"
},
{
"when": "resourceLangId == json",
"command": "contract.copyByteCode",
"group": "9_copyFromContractGroup"
},
{
"when": "resourceLangId == json",
"command": "contract.copyDeployedByteCode",
"group": "9_copyFromContractGroup"
},
{
"when": "resourceLangId == json",
"command": "contract.copyABI",
"group": "9_copyFromContractGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateMicroservicesWorkflows",
"group": "9_generationGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateDataPublishingWorkflows",
"group": "9_generationGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateEventPublishingWorkflows",
"group": "9_generationGroup"
},
{
"when": "resourceLangId == solidity",
"command": "azureBlockchainService.generateReportPublishingWorkflows",
"group": "9_generationGroup"
},
{
"when": "resourceLangId == solidity",
"command": "openZeppelin.addCategory",
"group": "8_buildContractGroup"
}
]
},
"views": {
"explorer": [
{
"id": "AzureBlockchain",
"name": "Azure Blockchain"
}
],
"debug": [
{
"id": "InstructionView",
"name": "Instructions"
}
]
},
"debuggers": [
{
"type": "truffle",
"label": "Truffle Debugger",
"program": "./out/src/debugger.js",
"runtime": "node",
"configurationAttributes": {
"launch": {
"required": [],
"properties": {
"stopOnEntry": {
"type": "boolean",
"description": "Automatically stop after launch.",
"default": true
},
"trace": {
"type": "boolean",
"description": "Enable logging of the Debug Adapter Protocol.",
"default": true
},
"txHash": {
"type": "string",
"description": "Transaction hash to debug",
"default": "0x"
},
"files": {
"type": "string[]",
"description": "Array of file paths of solidity files to debug",
"default": []
},
"workingDirectory": {
"type": "string",
"description": "Directory of truffle box",
"default": "${workspaceFolder}"
},
"providerUrl": {
"type": "string",
"description": "URL of provider",
"default": "http://127.0.0.1:8545"
}
}
}
},
"initialConfigurations": [
{
"type": "truffle",
"request": "launch",
"name": "Debug Transaction with Truffle",
"stopOnEntry": false,
"txHash": "0x",
"files": [],
"workingDirectory": "${workspaceFolder}",
"providerUrl": "http://127.0.0.1:8545"
}
],
"configurationSnippets": [
{
"label": "Truffle Debugger: Launch",
"description": "Runs the Truffle debugger (truffle) and attaches to a TestRPC instance",
"body": {
"type": "truffle",
"request": "launch",
"name": "Debug Transaction with Truffle",
"stopOnEntry": false,
"txHash": "0x",
"files": [],
"workingDirectory": "^\"\\${workspaceFolder}/",
"providerUrl": "http://127.0.0.1:8545"
}
}
],
"variables": {}
}
]
},
"scripts": {
"package": "npx vsce package",
"publish": "npx vsce publish",
"vscode:prepublish": "npm i && npm run webpack:prod && cd ./drizzleUI && npm i && npm run build:ext",
"compile": "npm run clean && tsc -p ./",
"webpack:prod": "webpack --mode production",
"watch": "tsc -watch -p ./",
"tslint": "tslint -t verbose src/**/*.ts test/**/*.ts",
"tslint:fix": "npm run tslint -- --fix",
"version": "tsc -v",
"test": "npm run compile && node ./out/test/runTest",
"clean": "npx rimraf -- ./out/*"
},
"devDependencies": {
"@types/astring": "^1.3.0",
"@types/estree": "^0.0.39",
"@types/download": "^6.2.4",
"@types/fs-extra": "^7.0.0",
"@types/glob": "^7.1.1",
"@types/hdkey": "^0.7.0",
"@types/istanbul": "^0.4.30",
"@types/mocha": "^5.2.7",
"@types/node": "^10.12.21",
"@types/request": "^2.48.1",
"@types/request-promise": "^4.1.44",
"@types/rewire": "^2.5.28",
"@types/semver": "^6.0.0",
"@types/sinon": "^7.0.11",
"@types/uuid": "^3.4.4",
"@types/vscode": "^1.36.1",
"copy-webpack-plugin": "^5.0.3",
"decache": "^4.5.1",
"glob": "^7.1.4",
"husky": "^2.4.0",
"istanbul": "^0.4.5",
"lint-staged": "^8.2.0",
"mocha": "^6.2.0",
"remap-istanbul": "^0.13.0",
"rewire": "^4.0.1",
"sinon": "^7.3.2",
"truffle": "^5.0.14",
"ts-loader": "6.2.1",
"tslint": "^5.18.0",
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.3.1",
"vscode-debugadapter": "^1.33.0",
"vscode-debugprotocol": "^1.33.0",
"vscode-test": "^1.2.0",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.6"
},
"dependencies": {
"@truffle/debug-utils": "1.0.19",
"@truffle/debugger": "5.0.35",
"@truffle/provider": "0.2.1",
"abi-decoder": "^2.0.1",
"acorn": "^6.1.1",
"acorn-walk": "^6.1.1",
"astring": "^1.4.0",
"azure-arm-resource": "^7.3.0",
"bip39": "^3.0.1",
"download": "^7.1.0",
"fs-extra": "^7.0.1",
"hdkey": "^1.1.1",
"ms-rest": "^2.5.0",
"ms-rest-azure": "^2.6.0",
"open": "^6.4.0",
"request": "^2.88.0",
"request-promise": "^4.2.4",
"semver": "^6.0.0",
"uuid": "^3.3.2",
"vscode-azureextensionui": "^0.25.1",
"vscode-extension-telemetry": "^0.1.1"
},
"extensionDependencies": [
"JuanBlanco.solidity",
"ms-vscode.azure-account"
],
"husky": {
"hooks": {
"pre-commit": "lint-staged && npm run compile"
}
},
"lint-staged": {
"*.ts": [
"npx tslint -t verbose",
"git add"
]
}
}

Просмотреть файл

@ -134,6 +134,7 @@ export class Constants {
azureBlockchainExtensionVersion: 'azureBlockchainExtensionVersion',
infuraCredentialsCacheKey: 'InfuraCache',
infuraExcludedProjectsListKey: 'InfuraExcludedProjects',
isNotifiedAboutOZSdk: 'isNotifiedAboutOZSdk',
mnemonicStorageKey: 'mnemonicStorage',
serviceResourceKey: 'treeContent',
};
@ -200,6 +201,11 @@ export class Constants {
yes: 'Yes',
};
public static installationDialogResult = {
cancel: 'Cancel',
install: 'Install',
};
public static mnemonicConstants = {
fileExt: 'env',
};
@ -578,6 +584,8 @@ export class Constants {
ContractNotDeployed: 'Contracts are not deployed. Please deploy first.',
DirectoryIsNotEmpty: 'Directory is not empty. Open another one?',
ErrorWhileExecutingCommand: 'Error while executing command: ',
ExtensionNotInstalled: (extensionName: string) =>
`Please install ${extensionName} extension or change user settings to use another extension`,
FetchingDeployedBytecodeIsFailed: 'An error occurred while fetching bytecode from network',
GetMessageChildAlreadyConnected: Constants.getMessageChildAlreadyConnected,
GitIsNotInstalled: 'Git is not installed',
@ -594,7 +602,7 @@ export class Constants {
NoContractBody: 'No contract body in AST',
NoSubscriptionFound: 'No subscription found.',
NoSubscriptionFoundClick: 'No subscription found, click an Azure account ' +
'at the bottom left corner and choose Select All',
'at the bottom left corner and choose Select All',
PleaseRenameOldStyleTruffleConfig: 'Please rename file "truffle.js" to "truffle-config.js"',
RequiredAppsAreNotInstalled: 'To run command you should install required apps',
SolidityContractsNotFound: 'Solidity contracts were not found',
@ -631,6 +639,7 @@ export class Constants {
memberNameValidating: 'Member name validating...',
networkIsNotReady: Constants.getNetworkIsNotReadyMessage,
openButton: 'Open',
ozFrameworkIsAvailableNow: 'You can now use OpenZeppelin framework as well as Truffle',
privateKeyWasCopiedToClipboard: 'Private key was copied to clipboard',
provisioningResource: (name: string) => `${name} is provisioning. The provisioning status can be viewed in the Azure portal. ` +
'You may return and complete this process once the provisioning is complete.',
@ -640,6 +649,8 @@ export class Constants {
signInButton: 'Sign In',
transactionBytecodeWasCopiedToClipboard: 'Transaction Bytecode was copied to clipboard',
transactionNodeNameValidating: 'Transaction Node name validating...',
unsupportedVersionOfExternalExtension: (name: string, currentVersion: string, supportedVersion: string) =>
`You try to use "${name}" extension of version ${currentVersion}, but current supported vesrion is ${supportedVersion}.`,
};
public static infuraCredentials = {
@ -731,9 +742,10 @@ export class Constants {
};
public static firstOZVersion = '2.3.0';
public static allOpenZeppelinVersions = ['2.3.0', '2.4.0'];
public static allOpenZeppelinVersions = ['2.3.0', '2.4.0', '2.5.0'];
public static userSettings = {
coreSdkSettingsKey: 'azureBlockchainService.coreSDK',
ozVersionUserSettingsKey: 'azureBlockchainService.openZeppelin.version',
storageAccountUserSettingsKey: 'azureBlockchainService.storageAccount.name',
};
@ -743,6 +755,11 @@ export class Constants {
queryTargetTypes: ['ContractProperties', 'ContractEvents'],
};
public static coreSdk = {
openZeppelin: 'OpenZeppelin',
truffle: 'Truffle',
};
public static openZeppelin = {
cancelButtonTitle: 'Cancel',
contractsUpgradeIsFailed: 'Upgrade of OpenZeppelin contracts has failed',
@ -800,6 +817,18 @@ export class Constants {
},
};
// TODO: add real extensions information when they will be released
public static externalExtensions: Record<string, any> = {
OpenZeppelin: {
commands: {
buildContracts: 'oz.build.command',
deployContracts: 'oz.deploy.command',
},
name: 'openZeppelin.extension.name',
supportedVersion: '1.0.0',
},
};
public static initialize(context: ExtensionContext) {
this.extensionContext = context;
this.temporaryDirectory = context.storagePath ? context.storagePath : os.tmpdir();

974
src/Constants.ts.orig Normal file
Просмотреть файл

@ -0,0 +1,974 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as os from 'os';
import * as path from 'path';
import { ExtensionContext, extensions } from 'vscode';
import { IOZAsset } from './services/openZeppelin/models';
const extensionId = 'AzBlockchain.azure-blockchain';
const packageJSON = extensions.getExtension(extensionId)!.packageJSON;
export enum RequiredApps {
node = 'node',
npm = 'npm',
git = 'git',
python = 'python',
truffle = 'truffle',
ganache = 'ganache-cli',
hdwalletProvider = 'truffle-hdwallet-provider',
}
export enum NotificationOptions {
error = 'error',
info = 'info',
warning = 'warning',
}
export class Constants {
public static extensionContext: ExtensionContext;
public static temporaryDirectory = '';
public static extensionName = packageJSON.name;
public static extensionVersion = packageJSON.version;
public static extensionKey = packageJSON.aiKey;
public static outputChannel = {
azureBlockchain: 'Azure Blockchain',
azureBlockchainServiceClient: 'Azure Blockchain Service Client',
executeCommand: 'Execute command',
ganacheCommands: 'Ganache Server',
logicAppGenerator: 'Logic App Generator',
requirements: 'Requirements',
telemetryClient: 'Telemetry Client',
treeManager: 'Service Tree Manager',
};
public static truffleConfigRequireNames = {
fs: 'fs',
fsPackageName: 'fs',
hdwalletProvider: 'HDWalletProvider',
};
public static truffleConfigDefaultDirectory = {
contracts_build_directory: path.join('./', 'build', 'contracts'),
contracts_directory: path.join('./', 'contracts'),
migrations_directory: path.join('./', 'migrations'),
};
public static defaultTruffleBox = 'Azure-Samples/Blockchain-Ethereum-Template';
public static defaultDebounceTimeout = 300;
public static defaultInputNameInBdm = 'transaction-node';
public static containerAzureBlockchainExtension = 'container-azureblockchainextension';
public static infuraHost = 'infura.io';
public static localhost = '127.0.0.1';
public static localhostName = 'development';
public static defaultLocalhostPort = 8545;
public static defaultABSPort = 3200;
public static defaultABSHost = 'blockchain.azure.com';
public static ganacheRetryTimeout = 2000; // milliseconds
public static ganacheRetryAttempts = 5;
public static azureBlockchainResourceName = {
eventGrid: 'Event Grid',
transactionNode: 'Transaction Node',
};
public static lengthParam = {
azureBlockchainResourceName: {
max: 20,
min: 2,
},
bdmName: {
max: 20,
min: 1,
},
eventGridName: {
max: 50,
min: 3,
},
password: {
max: 72,
min: 12,
},
resourceGroup: {
max: 90,
min: 1,
},
};
public static requiredVersions: { [key: string]: string | { min: string, max: string } } = {
[RequiredApps.ganache]: {
max: '7.0.0',
min: '6.0.0',
},
[RequiredApps.git]: '2.10.0',
[RequiredApps.hdwalletProvider]: {
max: '2.0.0',
min: '1.0.17',
},
[RequiredApps.node]: '10.15.0',
[RequiredApps.npm]: '6.4.1',
[RequiredApps.python]: {
max: '3.0.0',
min: '2.7.15',
},
[RequiredApps.truffle]: {
max: '6.0.0',
min: '5.0.0',
},
};
public static telemetryEvents = {
extensionActivated: 'Extension.Activated',
failedToCheckRequiredApps: 'Requirements.FailedToCheckRequiredApps',
webPages: {
action: 'WebPages.action',
disposeWebPage: 'WebPages.DisposeWebPage',
showWebPage: 'WebPages.ShowWebPage',
},
};
public static globalStateKeys = {
azureBlockchainExtensionVersion: 'azureBlockchainExtensionVersion',
infuraCredentialsCacheKey: 'InfuraCache',
infuraExcludedProjectsListKey: 'InfuraExcludedProjects',
mnemonicStorageKey: 'mnemonicStorage',
serviceResourceKey: 'treeContent',
};
public static infuraFileResponse = {
css: '',
path: '',
};
public static webViewPages = {
changelog: {
changelogPath: '',
path: '',
showOnStartup: 'showOnStartupChangelog',
title: 'Azure Blockchain Development Kit for Ethereum Changelog',
viewType: 'changelog',
},
contractUI: {
path: '',
showOnStartup: 'showOnStartupContractUI',
title: 'Smart Contract UI',
viewType: 'contractUIPage',
},
requirements: {
path: '',
showOnStartup: 'showOnStartupRequirementsPage',
title: 'Azure Blockchain Development Kit - Preview',
viewType: 'requirementsPage',
},
welcome: {
path: '',
showOnStartup: 'showOnStartupWelcomePage',
title: 'Welcome to Azure Blockchain',
viewType: 'welcomePage',
},
};
public static contractExtension = {
json: '.json',
sol: '.sol',
txt: '.txt',
};
public static networkProtocols = {
file: 'file://',
ftp: 'ftp://',
http: 'http://',
https: 'https://',
};
public static contractProperties = {
abi: 'abi',
bytecode: 'bytecode',
deployedBytecode: 'deployedBytecode',
};
public static propertyLabels = {
gasLimit: 'gas limit',
gasPrice: 'gas price',
};
public static confirmationDialogResult = {
no: 'No',
yes: 'Yes',
};
public static mnemonicConstants = {
fileExt: 'env',
};
public static defaultContractSettings = {
gasLimit: 4712388,
gasPrice: 100000000000,
};
public static paletteLabels = {
enterApplicationName: 'Enter application name',
enterBlockchainDataManagerName: 'Enter blockchain data manager name',
enterConnectionName: 'Enter connection name',
enterConsortiumManagementPassword: 'Enter consortium management password',
enterConsortiumName: 'Enter consortium name',
enterEventGridName: 'Enter event grid name',
enterInfuraProjectName: 'Enter project name',
enterLocalProjectName: 'Enter local project name',
enterLocalProjectPort: 'Enter local port number',
enterMemberName: 'Enter member name',
enterMemberPassword: 'Enter member password',
enterTransactionNodeName: 'Enter transaction node name',
enterTransactionNodePassword: 'Enter transaction node password',
enterTruffleBoxName: 'Enter pre-built Truffle project',
enterUserEmail: 'Enter user email address',
enterUserName: 'Enter user name',
enterUserPassword: 'Enter user password',
provideResourceGroupName: 'Provide a resource group name',
selectConsortiumProtocol: 'Select protocol',
selectConsortiumRegion: 'Select region',
selectConsortiumSku: 'Select SKU',
selectResourceGroup: 'Select resource group',
valueOrDefault: Constants.getMessageValueOrDefault,
};
public static treeItemData = {
group: {
azure: {
member: {
contextValue: 'member',
iconPath: { dark: '', light: '' },
},
},
bdm: {
input: {
contextValue: 'inputGroup',
iconPath: { dark: '', light: '' },
label: 'Inputs',
},
output: {
contextValue: 'outputGroup',
iconPath: { dark: '', light: '' },
label: 'Outputs',
},
},
},
network: {
azure: {
contextValue: 'network',
iconPath: { dark: '', light: '' },
},
bdm: {
application: {
contextValue: 'bdmApplication',
iconPath: { dark: '', light: '' },
},
input: {
contextValue: 'input',
iconPath: { dark: '', light: '' },
},
output: {
contextValue: 'output',
iconPath: { dark: '', light: '' },
},
},
default: {
contextValue: 'network',
iconPath: { dark: '', light: '' },
},
infura: {
contextValue: 'network',
iconPath: { dark: '', light: '' },
},
local: {
contextValue: 'localnetwork',
iconPath: { dark: '', light: '' },
},
},
project: {
azure: {
contextValue: 'project',
iconPath: { dark: '', light: '' },
},
bdm: {
contextValue: 'bdmproject',
iconPath: { dark: '', light: '' },
},
default: {
contextValue: 'project',
iconPath: { dark: '', light: '' },
},
infura: {
contextValue: 'project',
iconPath: { dark: '', light: '' },
},
local: {
contextValue: 'localproject',
iconPath: { dark: '', light: '' },
},
},
service: {
azure: {
contextValue: 'service',
iconPath: { dark: '', light: '' },
label: 'Azure Blockchain Service',
prefix: 'abs',
},
bdm: {
contextValue: 'service',
iconPath: { dark: '', light: '' },
label: 'Blockchain Data Manager',
prefix: 'bdm',
},
default: {
contextValue: 'service',
iconPath: { dark: '', light: '' },
label: 'Default Service',
},
infura: {
contextValue: 'service',
iconPath: { dark: '', light: '' },
label: 'Infura Service',
prefix: 'inf',
},
local: {
contextValue: 'service',
iconPath: { dark: '', light: '' },
label: 'Local Service',
prefix: 'loc',
},
},
};
public static validationRegexps = {
array: /^\[.*\]$/g,
forbiddenChars: {
dotAtTheEnd: /^(?=.*[.]$).*$/g,
networkName: /[^0-9a-z]/g,
outboundConnectionName: /^(\d|[a-z])+$/g,
password: /[#`*"'\-%;,]/g,
resourceGroupName: /[#`*"'%;,!@$^&+=?\/<>|[\]{}:\\~]/g,
},
hasDigits: /(?=.*\d)/g,
infuraProjectname: /^([a-zA-Z]|\d|\s|[-_:]){3,}$/g,
isJsonFile: new RegExp(Constants.contractExtension.json + '$'),
isLowerCase: /^[a-z0-9_\-!@$^&()+=?\/<>|[\]{}:.\\~ #`*"'%;,]+$/g,
isUrl: /^(?:http(s)?:\/\/)?[\w:@.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=]+$/igm,
lowerCaseLetter: /(?=.*[a-z]).*/g,
moduleExportsTemplate: /{(.*)}$/g,
onlyNumber: /^(-\d+|\d+)$/g,
// tslint:disable-next-line: max-line-length
port: /^([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/,
specialChars: {
azureBlockchainResourceName: /^(?=^[a-z])[a-z0-9]+$/g,
bdmName:
new RegExp(`^([a-z0-9]){${Constants.lengthParam.bdmName.min},${Constants.lengthParam.bdmName.max}}$`, 'g'),
eventGridName: new RegExp(`^([a-zA-Z0-9\-]){${Constants.lengthParam.eventGridName.min},${Constants.lengthParam.eventGridName.max}}$`, 'g'),
password: /[!@$^&()+=?\/<>|[\]{}_:.\\~]/g,
resourceGroupName: /[-\w.()]/g,
transactionNodeName:
new RegExp(`^(?=^[a-z])[a-z0-9]{${Constants.lengthParam.azureBlockchainResourceName.min},${Constants.lengthParam.azureBlockchainResourceName.max}}$`, 'g'),
},
types: {
simpleArray: /\w+\[\]/g,
simpleMapping: /^\[.+\]$/g,
solidityAddress: /^(0x)[a-zA-Z0-9]{40}$/g,
solidityInt: /^int\d+$/g,
solidityInteger: /u*int\d*/g,
solidityUint: /^uint\d+$/g,
},
upperCaseLetter: /(?=.*[A-Z]).*/g,
};
public static responseReason = {
alreadyExists: 'AlreadyExists',
};
public static validationMessages = {
arrayElementsShouldBeValid: (elementsType: string) => {
return `Array elements should have valid value of type ${elementsType}`;
},
bdmApplicationNameExist: 'Blockchain Data Manager Application name already exists.',
bdmNameAlreadyExists: 'Blockchain Data Manager name already exists.',
eventGridAlreadyExists: 'Event Grid name already exists.',
forbiddenChars: {
dotAtTheEnd: "Input value must not have '.' at the end.",
networkName: 'Invalid name. Name can contain only lowercase letters and numbers.',
outboundConnectionName: 'Outbound connection must contain only lowercase letters and numbers.',
password: "'#', '`', '*', '\"', ''', '-', '%', ',', ';'",
// tslint:disable-next-line: max-line-length
resourceGroupName: "'#', '`', '*', '\"', ''', '\%', ';', ',', '!', '@', '$', '^', '&', '+', '=', '?', '\/', '<', '>', '|', '[', '\]', '{', '}', ':', '\\', '~'",
},
forbiddenSymbols: 'Provided name has forbidden symbols.',
infuraProjectInvalidName: 'Project name must be at least 3 characters and should have alphanumeric, space, and the symbols "-", "_", ":".',
invalidAzureName: 'Invalid name. Name can contain only lowercase letters and numbers. ' +
`The first character must be a letter. Length must be between ${Constants.lengthParam.azureBlockchainResourceName.min} ` +
`and ${Constants.lengthParam.azureBlockchainResourceName.max} characters.`,
invalidBDMApplicationName: 'The Blockchain Data Manager Application name is invalid. Name can contain only lowercase letters and numbers. Length must ' +
`be between ${Constants.lengthParam.bdmName.min} and ${Constants.lengthParam.bdmName.max} characters.`,
invalidBlockchainDataManagerName:
'The Blockchain Data Manager name is invalid. Name can contain only lowercase letters and numbers. Length must ' +
`be between ${Constants.lengthParam.bdmName.min} and ${Constants.lengthParam.bdmName.max} characters.`,
invalidConfirmationResult: '\'yes\' or \'no\'',
invalidEventGridName: 'The Event Grid name is invalid. Name can contain only letters, numbers and dashes. ' +
`Length must be between ${Constants.lengthParam.eventGridName.min} and ${Constants.lengthParam.eventGridName.max} characters.`,
invalidHostAddress: 'Invalid host address',
invalidPort: 'Invalid port.',
invalidResourceGroupName: 'Resource group names only allow alphanumeric characters, periods,' +
'underscores, hyphens and parenthesis and cannot end in a period. ' +
`Length must be between ${Constants.lengthParam.resourceGroup.min} and ${Constants.lengthParam.resourceGroup.max} characters.`,
lengthRange: Constants.getMessageLengthRange,
nameAlreadyInUse: 'This name is already in use. Choose another one.',
noDigits: 'Password should have at least one digit.',
noLowerCaseLetter: 'Password should have at least one lowercase letter from a to z.',
noSpecialChars: 'Password must have 1 special character.',
noUpperCaseLetter: 'Password should have at least one uppercase letter from A to Z.',
onlyLowerCaseAllowed: 'Only lower case allowed.',
onlyNumberAllowed: 'Value after \':\' should be a number.',
openZeppelinFilesAreInvalid: Constants.getMessageOpenZeppelinFilesAreInvalid,
portAlreadyInUse: 'This port is already in use. Choose another one.',
portNotInUseGanache: 'No local service running on port. Please start service or select another port.',
projectAlreadyExists: 'Network already exists.',
projectAlreadyExistsOnInfura: 'Project already exist with the same name on Infura.',
projectIdAlreadyExists: 'Network with project ID already exists.',
resourceGroupAlreadyExists: Constants.getMessageResourceGroupAlreadyExist,
transactionNodeNameAlreadyExists: 'Transaction Node name already exists.',
unresolvedSymbols: Constants.getMessageInputHasUnresolvedSymbols,
valueCanSafelyStoreUpToBits: (pow: string) => {
return `Value can only safely store up to ${pow} bits`;
},
valueCannotBeEmpty: 'Value cannot be empty.',
valueShouldBeArray: 'Value should be the array and enclosed in \[ \]',
valueShouldBeBool: 'Value should be true or false.',
valueShouldBeNumber: 'Value should be a number.',
valueShouldBeNumberOrEmpty: 'Value should be a number or empty.',
valueShouldBePositiveAndCanSafelyStoreUpToBits: (pow: string) => {
return `Value should be positive and can only safely store up to ${pow} bits`;
},
valueShouldBeSolidityAddress: 'Value should be the correct solidity address.',
};
public static placeholders = {
confirmDialog: 'Are your sure?',
confirmPaidOperation: 'This operation will cost Ether, type \'yes\' to continue',
emptyLineText: '<empty line>',
generateMnemonic: 'Generate mnemonic',
pasteMnemonic: 'Paste mnemonic',
resourceGroupName: 'Resource Group Name',
selectBlockchainDataManagerInstance: 'Select Blockchain Data Manager instance',
selectConsortium: 'Select consortium',
selectContract: 'Select contract',
selectDeployDestination: 'Select deploy destination',
selectDestination: 'Select destination',
selectEventGrid: 'Select event grid',
selectGanacheServer: 'Select Ganache server',
selectInfuraProject: 'Select Infura project',
selectInfuraProjectAvailability: 'Select Infura project availability',
selectMember: 'Select member',
selectMnemonicExtractKey: 'Select mnemonic to extract key',
selectMnemonicStorage: 'Select mnemonic storage',
selectNetwork: 'Select network',
selectNewProjectPath: 'Select new project path',
selectProjects: 'Select Projects',
selectResourceGroup: 'Select a resource group',
selectRgLocation: 'Select a location to create your Resource Group in...',
selectSubscription: 'Select subscription',
selectTransactionNode: 'Select transaction node',
selectTypeOfSolidityProject: 'Select type of solidity project',
setupMnemonic: 'Setup mnemonic',
};
// More information see here
// https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids
public static infuraEndpointsIds: { [key: string]: number } = {
goerli: 5,
kovan: 42,
mainnet: 1,
rinkeby: 4,
ropsten: 3,
};
public static projectAvailability = {
private: 'Private',
public: 'Public',
};
public static consortiumMemberStatuses = {
ready: 'Ready',
};
public static executeCommandMessage = {
failedToRunCommand: (command: string) => `Failed to run command - ${command}. More details in output`,
failedToRunScript: (scriptPath: string) => `Failed to run script - ${scriptPath}. More details in output`,
finishRunningCommand: 'Finished running command',
forkingModule: 'Forking script',
runningCommand: 'Running command',
};
public static typeOfSolidityProject = {
action: {
emptyProject: 'createEmptyProject',
projectFromTruffleBox: 'createProjectFromTruffleBox',
},
text: {
emptyProject: 'Create basic project',
projectFromTruffleBox: 'Create Project from Truffle box',
},
};
public static statusBarMessages = {
buildingContracts: 'Building contracts',
checkingRequirementDependencies: 'Checking requirement dependencies version',
createBDMApplication: 'Creating BDM app',
createBlobs: 'Creating blobs',
createContainer: 'Creating container',
createStorageAccount: 'Creating storage account',
creatingBlockchainDataManager: 'Creating new Blockchain Data Manager',
creatingConsortium: 'Creating new consortium',
creatingEventGrid: 'Creating new event grid',
creatingProject: 'Creating new project',
deleteBlobs: 'Deleting blobs',
deployingContracts: (destination: string) => {
return `Deploying contracts to '${destination}'`;
},
generatingLogicApp: (appName: string) => `Generating ${appName}!`,
};
public static rpcMethods = {
getCode: 'eth_getCode',
netListening: 'net_listening',
netVersion: 'net_version',
};
public static ganacheCommandStrings = {
cannotStartServer: 'Cannot start ganache server',
ganachePortIsBusy: 'Cannot start ganache server, port is busy',
invalidGanachePort: 'Cannot start Ganache server. Invalid port',
serverAlreadyRunning: 'Ganache server already running',
serverCanNotRunWithoutGanache: 'To start a local server, installed ganache-cli is required',
serverCanNotStop: 'Ganache stop server was failed because is not ganache application',
serverNoGanacheAvailable: 'No Ganache network settings available',
serverNoGanacheInstance: 'No Ganache instance running',
serverSuccessfullyStarted: 'Ganache server successfully started',
serverSuccessfullyStopped: 'Ganache server successfully stopped',
};
public static uiCommandStrings = {
createBlockchainDataManagerProject: '$(plus) Create Blockchain Data Manager Project',
createConsortium: '$(plus) Create Consortium',
createEventGrid: '$(plus) Create Event Grid',
createInfuraProject: '$(plus) Create Infura Project',
createProject: '$(plus) Create a new network',
createTransactionNode: '$(plus) Create Transaction Node',
deployToConsortium: 'Deploy to consortium',
};
public static errorMessageStrings = {
// TODO names to lower case
ActionAborted: 'Action aborted',
AstIsEmpty: 'enums could not be extracted, current AST is empty',
BlockchainItemIsUnavailable: Constants.getNetworkIsNotAvailableMessage,
BuildContractsBeforeGenerating: 'Please build contracts before generating',
BuildContractsDirDoesNotExist: Constants.getMessageContractsBuildDirectoryDoesNotExist,
BuildContractsDirIsEmpty: Constants.getMessageContractsBuildDirectoryIsEmpty,
CompiledContractIsMissing: 'Compiled contract is missing for solidity file.',
ContractNotDeployed: 'Contracts are not deployed. Please deploy first.',
DirectoryIsNotEmpty: 'Directory is not empty. Open another one?',
ErrorWhileExecutingCommand: 'Error while executing command: ',
FetchingDeployedBytecodeIsFailed: 'An error occurred while fetching bytecode from network',
GetMessageChildAlreadyConnected: Constants.getMessageChildAlreadyConnected,
GitIsNotInstalled: 'Git is not installed',
IncorrectInputUrl: 'Incorrect input url',
InfuraUnauthorized: 'Unauthorized: please sign in with Infura account.',
InvalidContract: 'This file is not a valid contract.',
InvalidMnemonic: 'Invalid mnemonic',
LoadServiceTreeFailed: 'Load service tree has failed.',
MnemonicFileHaveNoText: 'Mnemonic file have no text',
NetworkAlreadyExist: Constants.getMessageNetworkAlreadyExist,
NetworkIsNotAvailable: 'The network the contract is deployed to is not available. Please deploy again.',
NetworkNotFound: Constants.getMessageNetworkNotFound,
NewProjectCreationFailed: 'Command createProject has failed.',
NoContractBody: 'No contract body in AST',
NoSubscriptionFound: 'No subscription found.',
NoSubscriptionFoundClick: 'No subscription found, click an Azure account ' +
'at the bottom left corner and choose Select All',
PleaseRenameOldStyleTruffleConfig: 'Please rename file "truffle.js" to "truffle-config.js"',
RequiredAppsAreNotInstalled: 'To run command you should install required apps',
SolidityContractsNotFound: 'Solidity contracts were not found',
SubscriptionNotFound: 'Can not find available subscription.',
ThereAreNoMnemonics: 'There are no mnemonics',
TruffleConfigHasIncorrectFormat: '"truffle-config.js" has incorrect format',
TruffleConfigIsNotExist: 'Truffle configuration file not found',
VariableShouldBeDefined: Constants.getMessageVariableShouldBeDefined,
WaitForLogin: 'You should sign-in on Azure Portal',
WorkflowTypeDoesNotMatch: 'workflowType does not match any available workflows',
WorkspaceShouldBeOpened: 'Workspace should be opened',
};
public static informationMessage = {
bdm: {
bdmApplicationNotReady: 'Creations of BDM application and dependent resources were started. You can view the status in the status bar below. ' +
'Please do not close vscode until it is ready. Once ready it will be added to the tree.',
contractMustBeDeployedForBDMApplication: 'The contract must be compiled and deployed before a BDM application can be created.',
},
cancelButton: 'Cancel',
compileAndDeployButton: 'Compile and deploy',
consortiumDoesNotHaveMemberWithUrl: 'Consortium does not have member with url',
consortiumNameValidating: 'Consortium name validating...',
contractNotDeployed: 'Contract not deployed yet.',
deployButton: 'Deploy',
deployFailed: 'Deploy failed',
deploySucceeded: 'Deploy succeeded',
detailsButton: 'Details',
generatedLogicApp: (appName: string) => `Generated the ${appName}!`,
infuraAccountSuccessfullyCreated: 'Your Infura account successfully created. Please check you email for complete registration',
infuraSignInPrompt: 'Not signed in to Infura account, sign in first.',
installButton: 'Install',
invalidRequiredVersion: 'Required app is not installed or has an old version.',
memberNameValidating: 'Member name validating...',
networkIsNotReady: Constants.getNetworkIsNotReadyMessage,
openButton: 'Open',
privateKeyWasCopiedToClipboard: 'Private key was copied to clipboard',
provisioningResource: (name: string) => `${name} is provisioning. The provisioning status can be viewed in the Azure portal. ` +
'You may return and complete this process once the provisioning is complete.',
requiresDependency: 'This project deployment requires the truffle-hdwallet-provider.',
rpcEndpointCopiedToClipboard: 'RPCEndpointAddress copied to clipboard',
seeDetailsRequirementsPage: 'Please see details on the Requirements Page',
signInButton: 'Sign In',
transactionBytecodeWasCopiedToClipboard: 'Transaction Bytecode was copied to clipboard',
transactionNodeNameValidating: 'Transaction Node name validating...',
};
public static infuraCredentials = {
clientId: 'vs-code',
clientSecret: 'pRo64S3izL72crOsuZ9PatRad0og5dlB',
scopes: {
offline: 'offline',
projectRead: 'projects.read',
projectWrite: 'projects.write',
userRead: 'user.read',
},
};
public static infuraAuthUrls = {
authURL: 'oauth2/auth',
baseURL: 'https://oauth.infura.io/',
callbackURL: 'http://127.0.0.1:9010/callback',
revoke: 'oauth2/revoke',
tokenURL: 'oauth2/token',
};
public static infuraAPIUrls = {
projects: 'eth/projects',
rootURL: 'https://system.infura.io/',
userMe: 'user/me',
};
public static infuraSigningIn = 'Signing in';
public static infuraRequestGrantType = {
authorizationCode: 'authorization_code',
refreshToken: 'refresh_token',
};
public static microservicesWorkflows = {
Data: 'Data',
Messaging: 'Messaging',
Reporting: 'Reporting',
Service: 'Service',
};
public static azureApps = {
AzureFunction: { label: 'Azure Function', serviceType: 2, outputDir: 'generatedAzureFunction' },
FlowApp: { label: 'Flow App', serviceType: 0, outputDir: 'generatedFlowApp' },
LogicApp: { label: 'Logic App', serviceType: 1, outputDir: 'generatedLogicApp' },
};
public static azureApiVersions = {
preview20180601: '2018-06-01-preview',
preview20190601: '2019-06-01-preview',
preview20200101: '2020-01-01-preview',
20190601: '2019-06-01',
};
public static provisioningState = {
creating: 'Creating',
failed: 'Failed',
resolvingDns: 'ResolvingDns',
stopped: 'Stopped',
succeeded: 'Succeeded',
updating: 'Updating',
};
public static availableBlockchainDataManagerLocations = ['eastus', 'westeurope'];
public static azureProviders = {
blockchain: 'Microsoft.Blockchain',
eventGrid: 'Microsoft.EventGrid',
storage: 'Microsoft.Storage',
};
public static azureResourceExplorer = {
contentType: 'application/json',
portalBasUri: 'https://ms.portal.azure.com/#@microsoft.onmicrosoft.com',
portalBladeUri: 'https://ms.portal.azure.com/#blade/ManagedLedgerExtension/TransactionNodeMenuBlade',
providerName: 'Microsoft.Blockchain',
requestAcceptLanguage: 'en-US',
requestBaseUri: 'https://management.azure.com',
resourceType: 'blockchainMembers',
xMsBlockBlobType: 'BlockBlob',
};
public static solidityTypes = {
address: 'address',
bool: 'bool',
int: 'int',
string: 'string',
uint: 'uint',
};
public static firstOZVersion = '2.3.0';
public static allOpenZeppelinVersions = ['2.3.0', '2.4.0'];
public static userSettings = {
ozVersionUserSettingsKey: 'azureBlockchainService.openZeppelin.version',
storageAccountUserSettingsKey: 'azureBlockchainService.storageAccount.name',
};
public static bdmApplicationRequestParameters = {
artifactType: 'EthereumSmartContract',
queryTargetTypes: ['ContractProperties', 'ContractEvents'],
};
public static openZeppelin = {
cancelButtonTitle: 'Cancel',
contractsUpgradeIsFailed: 'Upgrade of OpenZeppelin contracts has failed',
contactParameterInformation(contractName: string, parameterName: string, parameterType: string) {
return `Contract: ${contractName}. Parameter: ${parameterName}: ${parameterType}`;
},
descriptionDownloadingFailed: 'Description downloading failed',
downloadingContractsFromOpenZeppelin: 'Downloading contracts from OpenZeppelin',
exploreDownloadedContractsInfo: 'Explore more information about the contracts downloaded',
invalidVersionException: 'Invalid version. All OpenZeppelin work will be stopped',
moreDetailsButtonTitle: 'More details',
newVersionAvailable: 'There is a new version of your OpenZeppelin contracts available. Would you like to download the latest version?',
overwriteExistedContracts: 'Overwrite existed contracts',
projectFileName: 'project.json',
replaceButtonTitle: 'Replace',
retryButtonTitle: 'Retry',
retryDownloading: 'Retry downloading',
saveSpecifiedParameters: 'Not all contract parameters were defined. Do you want to save the progress?',
selectCategoryForDownloading: 'Select category for downloading',
skipButtonTitle: 'Skip files',
specifyContractParameters: 'Some contracts have parameters required for deploy. Do you want to specify them?',
upgradeOpenZeppelin: 'Upgrading OpenZeppelin',
hashCalculationFailed(errorMessage: string): string {
return `Error while calculating file hash. Message: ${errorMessage}`;
},
wereNotDownloaded(count: number): string {
return `OpenZeppelin: Some files (${count}) were not downloaded`;
},
wereDownloaded(count: number): string {
return `OpenZeppelin: (${count}) files were stored`;
},
alreadyExisted(existing: IOZAsset[]): string {
return `OpenZeppelin: (${existing.length}) files already exist on disk: `
+ existing.slice(0, 3).map((contract) => contract.name).join(' ')
+ (existing.length > 3 ? '...' : '');
},
invalidHashMessage(contractPath: string): string {
return `${contractPath} - invalid hash`;
},
validHashMessage(contractPath: string): string {
return `${contractPath} - valid hash`;
},
contractNotExistedOnDisk(contractPath: string): string {
return `${contractPath} - not existed on disk`;
},
categoryWillDownloaded(categoryName: string): string {
return `OpenZeppelin category will be downloaded: ${categoryName}`;
},
fileNow(count: number): string {
return `${count} file(s) on OpenZeppelin library now`;
},
invalidVersionDialog(version: string, location: string, lastVersion: string) {
return `There is invalid OpenZeppelin version (${version}) in ${location}. ` +
`Do you want to use the latest one (${lastVersion})?`;
},
};
public static initialize(context: ExtensionContext) {
this.extensionContext = context;
this.temporaryDirectory = context.storagePath ? context.storagePath : os.tmpdir();
this.webViewPages.contractUI.path = context.asAbsolutePath(path.join('resources', 'drizzle', 'index.html'));
this.webViewPages.welcome.path = context.asAbsolutePath(path.join('resources', 'welcome', 'index.html'));
this.webViewPages.requirements.path = context.asAbsolutePath(path.join('resources', 'welcome', 'prereqs.html'));
this.webViewPages.changelog.path = context.asAbsolutePath(
path.join('resources', 'welcome', 'changelog.html'));
this.webViewPages.changelog.changelogPath = context.asAbsolutePath(path.join('CHANGELOG.md'));
this.infuraFileResponse.path = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'index.html'));
this.infuraFileResponse.css = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'main.css'));
this.treeItemData.group.azure.member.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'ABS-member.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'ABS-member.svg')),
};
this.treeItemData.group.bdm.input.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManagerGroupInput.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManagerGroupInput.svg')),
};
this.treeItemData.group.bdm.output.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManagerGroupOutput.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManagerGroupOutput.svg')),
};
this.treeItemData.network.default.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'EthereumNetwork.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'EthereumNetwork.svg')),
};
this.treeItemData.network.azure.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'ABNetwork.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'ABNetwork.svg')),
};
this.treeItemData.network.bdm.application.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManagerApplication.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManagerApplication.svg')),
};
this.treeItemData.network.bdm.input.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'ABNetwork.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'ABNetwork.svg')),
};
this.treeItemData.network.bdm.output.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManagerOutput.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManagerOutput.svg')),
};
this.treeItemData.network.infura.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'EthereumNetwork.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'EthereumNetwork.svg')),
};
this.treeItemData.network.local.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'LocalNetwork.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'LocalNetwork.svg')),
};
this.treeItemData.project.azure.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'ABS-consortium.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'ABS-consortium.svg')),
};
this.treeItemData.project.bdm.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManager-service_and_project.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManager-service_and_project.svg')),
};
this.treeItemData.project.infura.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'InfuraProject.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'InfuraProject.svg')),
};
this.treeItemData.project.local.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'LocalProject.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'LocalProject.svg')),
};
this.treeItemData.service.azure.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'ABS-service.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'ABS-service.svg')),
};
this.treeItemData.service.bdm.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'BlockchainDataManager-service_and_project.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'BlockchainDataManager-service_and_project.svg')),
};
this.treeItemData.service.infura.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'InfuraService.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'InfuraService.svg')),
};
this.treeItemData.service.local.iconPath = {
dark: context.asAbsolutePath(path.join('resources/dark', 'LocalService.svg')),
light: context.asAbsolutePath(path.join('resources/light', 'LocalService.svg')),
};
}
public static getTransactionNodeName(memberName: string, transactionNodeName: string): string {
return memberName === transactionNodeName ? Constants.defaultInputNameInBdm : transactionNodeName;
}
private static getMessageChildAlreadyConnected(consortium: string): string {
return `Connection to '${consortium}' already exists`;
}
private static getMessageValueOrDefault(valueName: string, defaultValue: any): string {
return `Enter ${valueName}. Default value is `
+ `${defaultValue}. `
+ 'Press Enter for default.';
}
private static getMessageResourceGroupAlreadyExist(resourceGroupName: string): string {
return `A resource group with the same name: ${resourceGroupName} already exists. Please select other name`;
}
private static getMessageContractsBuildDirectoryIsEmpty(buildDirPath: string): string {
return `Contracts build directory "${buildDirPath}" is empty.`;
}
private static getMessageContractsBuildDirectoryDoesNotExist(buildDirPath: string): string {
return `Contracts build directory "${buildDirPath}" does not exist.`;
}
private static getMessageNetworkAlreadyExist(networkName: string): string {
return `Network with name "${networkName}" already existed in truffle-config.js`;
}
private static getMessageNetworkNotFound(networkName: string): string {
return `Network with name "${networkName}" not found in truffle-config.js`;
}
private static getMessageVariableShouldBeDefined(variable: string): string {
return `${variable} should be defined`;
}
private static getMessageLengthRange(min: number, max: number): string {
return `Length must be between ${min} and ${max} characters`;
}
private static getMessageInputHasUnresolvedSymbols(unresolvedSymbols: string): string {
return `Input value must not have '${unresolvedSymbols}'.`;
}
private static getMessageOpenZeppelinFilesAreInvalid(invalidFilePaths: string[]): string {
return `OpenZeppelin files have been modified or removed:
${invalidFilePaths.join('; ')}. Please revert changes or download them again.`;
}
private static getNetworkIsNotReadyMessage(itemType: string) {
switch (itemType) {
case 'AzureBlockchainNetworkNode':
return 'Azure Blockchain Service item is not ready yet. Please wait.';
default:
return 'Blockchain item is not ready yet. Please wait.';
}
}
private static getNetworkIsNotAvailableMessage(itemType: string) {
switch (itemType) {
case 'AzureBlockchainNetworkNode':
return 'Azure Blockchain Service item is unavailable.';
default:
return 'Blockchain item is unavailable.';
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getActionResponseTemplate() {
const runAfter: { [key: string]: any; } = {};
return {
inputs: {
body: '',
statusCode: 200,
},
kind: 'Http',
runAfter,
type: 'Response',
};
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getCaseTemplate() {
return {
actions: {},
case: '',
};
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getDefinitionTemplate() {
const properties: { [key: string]: any; } = {};
return {
// tslint:disable-next-line:max-line-length
$schema: 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#',
actions: {},
contentVersion: '1.0.0.0',
outputs: {},
parameters: {
$connections: {
defaultValue: {},
type: 'Object',
},
},
triggers: {
manual: {
inputs: {
schema: {
properties,
type: 'object',
},
},
kind: 'Http',
type: 'Request',
},
},
};
}

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getActionFlowAppTemplate() {
return {
inputs: {
host: {
apiId: '',
connectionName: '',
operationId: '',
},
parameters: {
abi: {},
contractAddress: '',
functionName: '',
} as { [key: string]: any },
},
runAfter: {},
type: 'OpenApiConnection',
};
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getClientDataTemplate() {
const connectionReferences: { [key: string]: any; } = {};
return {
properties: {
apiId: '/providers/Microsoft.PowerApps/apis/shared_logicflows',
connectionReferences,
definition: {},
displayName: '',
},
schemaVersion: '1.0.0.0',
};
}

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getConnectionReferenceTemplate() {
return {
connectionName: 'shared-blockchaineth-00000000-0000-0000-0000-000000000000',
id: '',
source: 'Embedded',
tier: 'NotSpecified',
};
}

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getFlowAppTemplate() {
return {
category: 5,
clientdata: '',
description: '',
name: '',
primaryentity: 'none',
statecode: 0,
type: 1,
};
}

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getActionLogicAppTemplate() {
return {
inputs: {
body: {},
host: {
connection: {
name: '',
},
},
method: 'post',
path: '',
queries: {
abi: '',
contractAddress: '',
},
},
runAfter: {},
type: 'ApiConnection',
};
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getConnectionValueTemplate() {
return {
connectionId: '',
connectionName: '',
id: '',
};
}

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getLogicAppTemplate() {
const value: { [key: string]: any; } = {};
return {
definition: {},
parameters: {
$connections: {
value,
},
},
};
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export function getSwitchTemplate() {
const cases: { [key: string]: any; } = {};
return {
Switch: {
cases,
default: {
actions: {
DefaultResponse: {
inputs: {
body: 'Method or state variable not found.',
statusCode: 404,
},
kind: 'Http',
runAfter: {},
type: 'Response',
},
},
},
expression: '',
runAfter: {},
type: 'Switch',
},
};
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { getActionResponseTemplate } from './ActionResponse';
import { getCaseTemplate } from './Case';
import { getDefinitionTemplate } from './Definition';
import { getActionFlowAppTemplate } from './FlowApp/ActionFlowApp';
import { getClientDataTemplate } from './FlowApp/ClientData';
import { getConnectionReferenceTemplate } from './FlowApp/ConnectionReferences';
import { getFlowAppTemplate } from './FlowApp/FlowApp';
import { getActionLogicAppTemplate } from './LogicApp/ActionLogicApp';
import { getConnectionValueTemplate } from './LogicApp/ConnectionValue';
import { getLogicAppTemplate } from './LogicApp/LogicApp';
import { getSwitchTemplate } from './Switch';
export {
getActionResponseTemplate,
getCaseTemplate,
getDefinitionTemplate,
getActionFlowAppTemplate,
getClientDataTemplate,
getConnectionReferenceTemplate,
getFlowAppTemplate,
getActionLogicAppTemplate,
getConnectionValueTemplate,
getLogicAppTemplate,
getSwitchTemplate,
};

Просмотреть файл

@ -0,0 +1,161 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import {
getActionFlowAppTemplate,
getActionLogicAppTemplate,
getActionResponseTemplate,
getCaseTemplate,
getClientDataTemplate,
getConnectionReferenceTemplate,
getConnectionValueTemplate,
getDefinitionTemplate,
getFlowAppTemplate,
getLogicAppTemplate,
getSwitchTemplate,
} from './ApplicationDto';
import { parseSolidityContract } from './ContractParser';
const propertyFunctionName = 'methodOrState';
const propertyInputParameters = 'inputParameters';
const propertyBlockchainethereum = 'blockchainethereum';
export function generateLogicAppForMicroservice(
abi: string,
contractAddress: string,
subscriptionId: string,
location: string,
solFilePath: string,
) {
const { variables, functionsDefinitions } = parseSolidityContract(solFilePath);
const logicApp = getLogicAppTemplate();
const definition = getDefinitionTemplate();
const switchBlock = getSwitchTemplate();
for (const item of [...variables, ...functionsDefinitions]) {
const actions: { [key: string]: any } = {};
const action = getActionLogicAppTemplate();
const bodyParameters: { [key: string]: any } = {};
if (item.type === 'FunctionDefinition') {
for (const parameter of item.parameters) {
bodyParameters[parameter.name] = `@triggerBody()?['${propertyInputParameters}']?['${parameter.name}']`;
}
action.inputs.path = `/contract/functions/@{encodeURIComponent(encodeURIComponent('${item.name}'))}/execute`;
} else {
action.inputs.path = `/contract/functions/@{encodeURIComponent(encodeURIComponent('${item.name}'))}/query`;
}
action.inputs.body = bodyParameters;
action.inputs.queries.abi = abi;
action.inputs.queries.contractAddress = contractAddress;
action.inputs.host.connection.name = `@parameters('$connections')['${propertyBlockchainethereum}']['connectionId']`;
const actionResponse = getActionResponseTemplate();
actionResponse.inputs.body = `@body('${item.name}')`;
actionResponse.runAfter[item.name!] = [ 'Succeeded' ];
actions[item.name!] = action;
actions[`${item.name}Response`] = actionResponse;
const caseBlock = getCaseTemplate();
caseBlock.case = item.name!;
caseBlock.actions = actions;
switchBlock.Switch.cases[`Case_${item.name}`] = caseBlock;
}
switchBlock.Switch.expression = `@triggerBody()?['${propertyFunctionName}']`;
definition.actions = switchBlock;
definition.triggers.manual.inputs.schema.properties[propertyInputParameters] = { type: 'object' };
definition.triggers.manual.inputs.schema.properties[propertyFunctionName] = { type: 'string' };
logicApp.definition = definition;
logicApp.parameters.$connections.value[propertyBlockchainethereum] = getConnectionValueTemplate();
logicApp.parameters.$connections.value[propertyBlockchainethereum].connectionId
= `/subscriptions/${subscriptionId}/resourceGroups/`;
logicApp.parameters.$connections.value[propertyBlockchainethereum].id
= `/subscriptions/${subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/blockchainethereum`;
return logicApp;
}
export function generateFlowAppForMicroservice(
name: string,
abi: string,
contractAddress: string,
solFilePath: string,
) {
const { variables, functionsDefinitions } = parseSolidityContract(solFilePath);
const flowApp = getFlowAppTemplate();
const definition = getDefinitionTemplate();
const switchBlock = getSwitchTemplate();
const connectionReferenceName = `shared_${propertyBlockchainethereum}`;
for (const item of [...variables, ...functionsDefinitions]) {
const actions: { [key: string]: any } = {};
const bodyParameters: { [key: string]: any } = {};
const action = getActionFlowAppTemplate();
const actionResponseData = getActionResponseTemplate();
if (item.type === 'FunctionDefinition') {
for (const parameter of item.parameters) {
bodyParameters[parameter.name] = `@triggerBody()?['${propertyInputParameters}']?['${parameter.name}']`;
action.inputs.parameters[`parameters/${parameter.name}`] = `${propertyInputParameters}.${parameter.name}`;
}
if (item.stateMutability === 'view' || item.stateMutability === 'pure') {
action.inputs.host.operationId = item.parameters.length ? 'ExecuteSmartContractFunction' : 'GetSmartContractProperties';
} else {
action.inputs.host.operationId = 'ExecuteContractFunction';
}
actionResponseData.inputs.body = item.returnParameters
? `@outputs('${item.name}')?['body/Function Output']`
: `@body('${item.name}')`;
} else {
action.inputs.host.operationId = 'GetSmartContractProperties';
actionResponseData.inputs.body = `@outputs('${item.name}')?['body/${item.name}']`;
}
action.inputs.host.connectionName = connectionReferenceName;
action.inputs.host.apiId = `/providers/Microsoft.PowerApps/apis/${connectionReferenceName}`;
action.inputs.parameters.contractAddress = contractAddress;
action.inputs.parameters.functionName = item.name!;
action.inputs.parameters.abi = abi;
actionResponseData.runAfter[item.name!] = [ 'Succeeded' ];
actions[item.name!] = action;
actions[`${item.name}Response`] = actionResponseData;
const caseBlock = getCaseTemplate();
caseBlock.case = item.name!;
caseBlock.actions = actions;
switchBlock.Switch.cases[`Case_${item.name}`] = caseBlock;
}
switchBlock.Switch.expression = `@triggerBody()?['${propertyFunctionName}']`;
definition.actions = switchBlock;
definition.triggers.manual.inputs.schema.properties[propertyInputParameters] = { type: 'object' };
definition.triggers.manual.inputs.schema.properties[propertyFunctionName] = { type: 'string' };
const clientData = getClientDataTemplate();
clientData.properties.definition = definition;
clientData.properties.displayName = name;
clientData.properties.connectionReferences[connectionReferenceName] = getConnectionReferenceTemplate();
clientData.properties.connectionReferences[connectionReferenceName].id = `/providers/Microsoft.PowerApps/apis/${connectionReferenceName}`;
flowApp.clientdata = JSON.stringify(clientData);
flowApp.name = name;
return flowApp;
}

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as fs from 'fs-extra';
import {
FunctionDefinition,
parse,
SourceUnit,
StateVariableDeclaration,
VariableDeclaration,
visit,
} from 'solidity-parser-antlr';
export function parseSolidityContract(solFilePath: string)
: { variables: VariableDeclaration[], functionsDefinitions: FunctionDefinition[] } {
const ast = parseFileToAstNode(solFilePath);
const functionArray: FunctionDefinition[] = [];
const stateVariableArray: VariableDeclaration[] = [];
visit(ast, {
StateVariableDeclaration(node: StateVariableDeclaration) {
for (const variable of node.variables) {
if (variable.visibility === 'public') {
stateVariableArray.push(variable);
}
}
},
FunctionDefinition(node: FunctionDefinition) {
if (!node.isConstructor && (node.visibility === 'public' || node.visibility === 'external')) {
functionArray.push(node);
}
},
});
return { variables: stateVariableArray, functionsDefinitions: functionArray };
}
function parseFileToAstNode(solFilePath: string) {
const solFileContent = fs.readFileSync(solFilePath, 'utf8');
return parse(solFileContent, {}) as SourceUnit;
}

Просмотреть файл

@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { Memento, window } from 'vscode';
import { Constants } from '../Constants';
import {
// getWorkspaceRoot,
// outputCommandHelper,
// showNotificationConfirmationDialog,
userSettings,
} from '../helpers';
import {
IExtensionAdapter,
OpenZeppelinExtensionAdapter,
TruffleExtensionAdapter,
} from '../services/extensionAdapter';
class SdkCoreCommands {
// @ts-ignore
private globalState?: Memento;
private extensionAdapter!: IExtensionAdapter;
public async initialize(globalState: Memento): Promise<void> {
this.globalState = globalState;
const sdk = await this.getCoreSdk();
this.extensionAdapter = this.getExtensionAdapter(sdk.userValue ? sdk.userValue : sdk.defaultValue);
this.extensionAdapter.validateExtension().catch((error) => {
window.showErrorMessage(error.message);
});
}
public async build(): Promise<void> {
// await this.notifyAboutOpenZeppelinSdk();
return this.extensionAdapter.build();
}
public async deploy(): Promise<void> {
// await this.notifyAboutOpenZeppelinSdk();
return this.extensionAdapter.deploy();
}
private async getCoreSdk() {
return userSettings.getConfigurationAsync(Constants.userSettings.coreSdkSettingsKey);
}
private getExtensionAdapter(sdk: string): IExtensionAdapter {
switch (sdk) {
case Constants.coreSdk.openZeppelin:
return new OpenZeppelinExtensionAdapter();
default:
return new TruffleExtensionAdapter();
}
}
// TODO: uncomment this once openzeppelin extension is ready
/* private async notifyAboutOpenZeppelinSdk() {
const isNotified = this.globalState!.get(Constants.globalStateKeys.isNotifiedAboutOZSdk);
if (!isNotified) {
const sdk = await this.getCoreSdk();
if (sdk.userValue !== Constants.coreSdk.openZeppelin) {
const answer = await showNotificationConfirmationDialog(
Constants.informationMessage.ozFrameworkIsAvailableNow,
Constants.installationDialogResult.install,
Constants.installationDialogResult.cancel);
if (answer) {
await outputCommandHelper.executeCommand(
getWorkspaceRoot(true),
`code --install-extension ${Constants.externalExtensions.openZeppelin.name}@'+
`${Constants.externalExtensions.openZeppelin.supportedVersion}`);
await userSettings.updateConfigurationAsync(
Constants.userSettings.coreSdkSettingsKey,
Constants.coreSdk.openZeppelin);
}
}
await this.globalState!.update(Constants.globalStateKeys.isNotifiedAboutOZSdk, true);
await this.initialize(this.globalState!);
}
} */
}
export const sdkCoreCommands = new SdkCoreCommands();

Просмотреть файл

@ -10,3 +10,4 @@ export * from './OpenZeppelinCommands';
export * from './ProjectCommands';
export * from './ServiceCommands';
export * from './TruffleCommands';
export * from './SdkCoreCommands';

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export * from './ContractCommands';
export * from './DebuggerCommands';
export * from './GanacheCommands';
export * from './InfuraCommands';
export * from './LogicAppCommands';
export * from './OpenZeppelinCommands';
export * from './ProjectCommands';
export * from './ServiceCommands';
export * from './TruffleCommands';

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { commands, ExtensionContext, Uri, window } from 'vscode';
import { commands, ExtensionContext, Uri, window, workspace } from 'vscode';
import {
ContractCommands,
DebuggerCommands,
@ -10,6 +10,7 @@ import {
LogicAppCommands,
OpenZeppelinCommands,
ProjectCommands,
sdkCoreCommands,
ServiceCommands,
TruffleCommands,
} from './commands';
@ -44,6 +45,7 @@ export async function activate(context: ExtensionContext) {
MnemonicRepository.initialize(context.globalState);
TreeManager.initialize(context.globalState);
TreeService.initialize('AzureBlockchain');
await sdkCoreCommands.initialize(context.globalState);
setCommandContext(CommandContext.Enabled, true);
setCommandContext(CommandContext.IsWorkspaceOpen, isWorkspaceOpen());
@ -84,11 +86,11 @@ export async function activate(context: ExtensionContext) {
const newSolidityProject = commands.registerCommand('truffle.newSolidityProject', async () => {
await tryExecute(() => ProjectCommands.newSolidityProject());
});
const buildContracts = commands.registerCommand('truffle.buildContracts', async () => {
await tryExecute(() => TruffleCommands.buildContracts());
const buildContracts = commands.registerCommand('azureBlockchainService.buildContracts', async () => {
await tryExecute(() => sdkCoreCommands.build());
});
const deployContracts = commands.registerCommand('truffle.deployContracts', async () => {
await tryExecute(() => TruffleCommands.deployContracts());
const deployContracts = commands.registerCommand('azureBlockchainService.deployContracts', async () => {
await tryExecute(() => sdkCoreCommands.deploy());
});
const copyByteCode = commands.registerCommand('contract.copyByteCode', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeBytecodeToBuffer(uri));
@ -186,6 +188,14 @@ export async function activate(context: ExtensionContext) {
});
//#endregion
//#region other subscriptions
const changeCoreSdkConfigurationListener = workspace.onDidChangeConfiguration(async (event) => {
if (event.affectsConfiguration(Constants.userSettings.coreSdkSettingsKey)) {
await sdkCoreCommands.initialize(context.globalState);
}
});
//#endregion
const subscriptions = [
showWelcomePage,
showRequirementsPage,
@ -216,6 +226,7 @@ export async function activate(context: ExtensionContext) {
showProjectsFromInfuraAccount,
openZeppelinAddCategory,
openAtAzurePortal,
changeCoreSdkConfigurationListener,
];
context.subscriptions.push(...subscriptions);

257
src/extension.ts.orig Normal file
Просмотреть файл

@ -0,0 +1,257 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { commands, ExtensionContext, Uri, window } from 'vscode';
import {
ContractCommands,
DebuggerCommands,
GanacheCommands,
InfuraCommands,
LogicAppCommands,
OpenZeppelinCommands,
ProjectCommands,
ServiceCommands,
TruffleCommands,
} from './commands';
import { Constants } from './Constants';
import { CommandContext, isWorkspaceOpen, openZeppelinHelper, required, setCommandContext } from './helpers';
import { CancellationEvent } from './Models';
import { Output } from './Output';
import { ChangelogPage, RequirementsPage, WelcomePage } from './pages';
import {
AdapterType,
ContractDB,
GanacheService,
InfuraServiceClient,
MnemonicRepository,
TreeManager,
TreeService,
} from './services';
import { Telemetry } from './TelemetryClient';
import { NetworkNodeView, ProjectView } from './ViewItems';
import { DebuggerConfiguration } from './debugAdapter/configuration/debuggerConfiguration';
export async function activate(context: ExtensionContext) {
if (process.env.CODE_TEST) {
return;
}
Constants.initialize(context);
DebuggerConfiguration.initialize(context);
await ContractDB.initialize(AdapterType.IN_MEMORY);
await InfuraServiceClient.initialize(context.globalState);
MnemonicRepository.initialize(context.globalState);
TreeManager.initialize(context.globalState);
TreeService.initialize('AzureBlockchain');
setCommandContext(CommandContext.Enabled, true);
setCommandContext(CommandContext.IsWorkspaceOpen, isWorkspaceOpen());
const welcomePage = new WelcomePage(context);
const requirementsPage = new RequirementsPage(context);
const changelogPage = new ChangelogPage(context);
await welcomePage.checkAndShow();
await changelogPage.checkAndShow();
//#region azureBlockchain extension commands
const refresh = commands.registerCommand('azureBlockchainService.refresh', (element) => {
TreeService.refresh(element);
});
const showWelcomePage = commands.registerCommand('azureBlockchainService.showWelcomePage', async () => {
return welcomePage.show();
});
const showRequirementsPage = commands.registerCommand('azureBlockchainService.showRequirementsPage',
async (checkShowOnStartup: boolean) => {
return checkShowOnStartup ? await requirementsPage.checkAndShow() : await requirementsPage.show();
});
//#endregion
//#region Ganache extension commands
const startGanacheServer = commands.registerCommand('azureBlockchainService.startGanacheServer',
async (viewItem?: ProjectView) => {
await tryExecute(() => GanacheCommands.startGanacheCmd(viewItem));
});
const stopGanacheServer = commands.registerCommand('azureBlockchainService.stopGanacheServer',
async (viewItem?: ProjectView) => {
await tryExecute(() => GanacheCommands.stopGanacheCmd(viewItem));
});
//#endregion
//#region truffle commands
const newSolidityProject = commands.registerCommand('truffle.newSolidityProject', async () => {
await tryExecute(() => ProjectCommands.newSolidityProject());
});
const buildContracts = commands.registerCommand('truffle.buildContracts', async () => {
await tryExecute(() => TruffleCommands.buildContracts());
});
const deployContracts = commands.registerCommand('truffle.deployContracts', async () => {
await tryExecute(() => TruffleCommands.deployContracts());
});
const copyByteCode = commands.registerCommand('contract.copyByteCode', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeBytecodeToBuffer(uri));
});
const copyDeployedByteCode = commands.registerCommand('contract.copyDeployedByteCode', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeDeployedBytecodeToBuffer(uri));
});
const copyABI = commands.registerCommand('contract.copyABI', async (uri: Uri) => {
await tryExecute(() => TruffleCommands.writeAbiToBuffer(uri));
});
const copyRPCEndpointAddress = commands.registerCommand('azureBlockchainService.copyRPCEndpointAddress',
async (viewItem: NetworkNodeView) => {
await tryExecute(() => TruffleCommands.writeRPCEndpointAddressToBuffer(viewItem));
});
const getPrivateKeyFromMnemonic = commands.registerCommand('azureBlockchainService.getPrivateKey', async () => {
await tryExecute(() => TruffleCommands.getPrivateKeyFromMnemonic());
});
//#endregion
//#region services with dialog
const createProject = commands.registerCommand('azureBlockchainService.createProject', async () => {
await tryExecute(() => ServiceCommands.createProject());
});
const connectProject = commands.registerCommand('azureBlockchainService.connectProject', async () => {
await tryExecute(() => ServiceCommands.connectProject());
});
const disconnectProject = commands.registerCommand('azureBlockchainService.disconnectProject',
async (viewItem: ProjectView) => {
await tryExecute(() => ServiceCommands.disconnectProject(viewItem));
});
const openAtAzurePortal = commands.registerCommand('azureBlockchainService.openAtAzurePortal',
async (viewItem: NetworkNodeView) => ServiceCommands.openAtAzurePortal(viewItem));
//#endregion
//#region Infura commands
const signInToInfuraAccount = commands.registerCommand('azureBlockchainService.signInToInfuraAccount', async () => {
await tryExecute(() => InfuraCommands.signIn());
});
const signOutOfInfuraAccount = commands.registerCommand('azureBlockchainService.signOutOfInfuraAccount', async () => {
await tryExecute(() => InfuraCommands.signOut());
});
const showProjectsFromInfuraAccount = commands.registerCommand(
'azureBlockchainService.showProjectsFromInfuraAccount',
async () => {
await tryExecute(() => InfuraCommands.showProjectsFromAccount());
});
//#endregion
//#region contract commands
const showSmartContractPage = commands.registerCommand(
'azureBlockchainService.showSmartContractPage',
async (contractPath: Uri) => {
await tryExecute(() => ContractCommands.showSmartContractPage(context, contractPath));
});
const createNewBDMApplication = commands.registerCommand('azureBlockchainService.createNewBDMApplication',
async (viewItem: ProjectView) => {
await tryExecute(() => ServiceCommands.createNewBDMApplication(viewItem));
});
const deleteBDMApplication = commands.registerCommand('azureBlockchainService.deleteBDMApplication',
async (viewItem: NetworkNodeView) => await tryExecute(() => ServiceCommands.deleteBDMApplication(viewItem)));
//#endregion
//#region open zeppelin commands
const openZeppelinAddCategory = commands.registerCommand('openZeppelin.addCategory', async () => {
await tryExecute(() => OpenZeppelinCommands.addCategory());
});
//#endregion
//#region logic app commands
const generateMicroservicesWorkflows = commands.registerCommand(
'azureBlockchainService.generateMicroservicesWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateMicroservicesWorkflows(filePath));
});
const generateDataPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateDataPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateDataPublishingWorkflows(filePath));
});
const generateEventPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateEventPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateEventPublishingWorkflows(filePath));
});
const generateReportPublishingWorkflows = commands.registerCommand(
'azureBlockchainService.generateReportPublishingWorkflows',
async (filePath: Uri | undefined) => {
await tryExecute(async () => await LogicAppCommands.generateReportPublishingWorkflows(filePath));
});
//#endregion
//#region debugger commands
const startDebugger = commands.registerCommand('extension.truffle.debugTransaction', async () => {
await tryExecute(() => DebuggerCommands.startSolidityDebugger());
});
//#endregion
const subscriptions = [
showWelcomePage,
showRequirementsPage,
showSmartContractPage,
refresh,
newSolidityProject,
buildContracts,
deployContracts,
createNewBDMApplication,
createProject,
connectProject,
deleteBDMApplication,
disconnectProject,
copyByteCode,
copyDeployedByteCode,
copyABI,
copyRPCEndpointAddress,
startGanacheServer,
stopGanacheServer,
generateMicroservicesWorkflows,
generateDataPublishingWorkflows,
generateEventPublishingWorkflows,
generateReportPublishingWorkflows,
getPrivateKeyFromMnemonic,
startDebugger,
signInToInfuraAccount,
signOutOfInfuraAccount,
showProjectsFromInfuraAccount,
openZeppelinAddCategory,
openAtAzurePortal,
];
context.subscriptions.push(...subscriptions);
required.checkAllApps();
Telemetry.sendEvent(Constants.telemetryEvents.extensionActivated);
checkAndUpgradeOpenZeppelinAsync();
}
export async function deactivate(): Promise<void> {
// This method is called when your extension is deactivated
// To dispose of all extensions, vscode provides 5 sec.
// Therefore, please, call important dispose functions first and don't use await
// For more information see https://github.com/Microsoft/vscode/issues/47881
GanacheService.dispose();
ContractDB.dispose();
Telemetry.dispose();
TreeManager.dispose();
Output.dispose();
}
async function tryExecute(func: () => Promise<any>, errorMessage: string | null = null): Promise<void> {
try {
await func();
} catch (error) {
if (error instanceof CancellationEvent) {
return;
}
window.showErrorMessage(errorMessage || error.message);
}
}
async function checkAndUpgradeOpenZeppelinAsync(): Promise<void> {
if (await openZeppelinHelper.shouldUpgradeOpenZeppelinAsync()) {
await openZeppelinHelper.upgradeOpenZeppelinUserSettingsAsync();
await openZeppelinHelper.upgradeOpenZeppelinContractsAsync();
}
}

Просмотреть файл

@ -151,6 +151,19 @@ export async function showIgnorableNotification(message: string, fn: () => Promi
});
}
export async function showNotificationConfirmationDialog(
message: string,
positiveAnswer: string,
cancel: string): Promise<boolean> {
const answer = await window.showInformationMessage(
message,
positiveAnswer,
cancel,
);
return answer === positiveAnswer;
}
namespace Notification {
export const types = {
error: window.showErrorMessage,

Просмотреть файл

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as fs from 'fs';
import { InputBoxOptions, ProgressLocation, QuickPickItem, QuickPickOptions, Uri, window, workspace } from 'vscode';
import { Constants, NotificationOptions } from '../Constants';
import { CancellationEvent } from '../Models';
import { Telemetry } from '../TelemetryClient';
import { DialogResultValidator } from '../validators/DialogResultValidator';
export async function showInputBox(options: InputBoxOptions): Promise<string> {
const result = await window.showInputBox(options);
if (result === undefined) {
Telemetry.sendEvent('userInteraction.showInputBox.userCancellation', { prompt: options.prompt || '' });
throw new CancellationEvent();
}
return result;
}
export async function showQuickPickMany<T extends QuickPickItem>(
items: T[] | Promise<T[]>,
options: QuickPickOptions & { canPickMany: true; },
): Promise<T[]> {
const result = await window.showQuickPick(items, options);
if (result === undefined) {
Telemetry.sendEvent(
'userInteraction.showQuickPickMany.userCancellation',
{ placeHolder: options.placeHolder || '' },
);
throw new CancellationEvent();
}
return result;
}
export async function showQuickPick<T extends QuickPickItem>(items: T[] | Promise<T[]>, options: QuickPickOptions)
: Promise<T> {
const result = await window.showQuickPick(items, options);
if (result === undefined) {
Telemetry.sendEvent('userInteraction.showQuickPick.userCancellation', { placeHolder: options.placeHolder || '' });
throw new CancellationEvent();
}
return result;
}
export async function showConfirmPaidOperationDialog() {
const answer = await showInputBox({
ignoreFocusOut: true,
prompt: Constants.placeholders.confirmPaidOperation,
validateInput: DialogResultValidator.validateConfirmationResult,
});
if (answer.toLowerCase() !== Constants.confirmationDialogResult.yes.toLowerCase()) {
Telemetry.sendEvent(
'userInteraction.showConfirmPaidOperationDialog.userCancellation',
{ prompt: Constants.placeholders.confirmPaidOperation });
throw new CancellationEvent();
}
}
export async function showOpenFolderDialog(): Promise<string> {
const folder = await window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: Constants.placeholders.selectNewProjectPath,
});
if (!folder) {
Telemetry.sendEvent(
'userInteraction.showOpenFolderDialog.userCancellation',
{ label: Constants.placeholders.selectNewProjectPath });
throw new CancellationEvent();
}
return folder[0].fsPath;
}
export async function showOpenFileDialog(): Promise<string> {
const defaultFolder = workspace.workspaceFolders ? workspace.workspaceFolders[0].uri.fsPath : '';
const folder = await window.showSaveDialog({
defaultUri: Uri.parse(defaultFolder),
saveLabel: Constants.placeholders.selectMnemonicStorage,
});
if (!folder) {
Telemetry.sendEvent(
'userInteraction.showOpenFileDialog.userCancellation',
{ label: Constants.placeholders.selectMnemonicStorage });
throw new CancellationEvent();
}
return folder.fsPath;
}
export async function saveTextInFile(
text: string,
defaultFilename: string,
ext?: { [name: string]: string[] },
): Promise<string> {
const file = await window.showSaveDialog({
defaultUri: Uri.file(defaultFilename),
filters: ext,
});
if (!file) {
Telemetry.sendEvent('userInteraction.saveTextInFile.userCancellation', { label: 'fileNotSelected' });
throw new CancellationEvent();
}
fs.writeFileSync(file.fsPath, text);
return file.fsPath;
}
export async function showConfirmationDialog(message: string): Promise<boolean> {
const answer = await window.showInformationMessage(
message,
Constants.confirmationDialogResult.yes,
Constants.confirmationDialogResult.no,
);
return answer === Constants.confirmationDialogResult.yes;
}
export async function showNotification(options: Notification.IShowNotificationOptions): Promise<void> {
options.type = options.type || NotificationOptions.info;
Notification.types[options.type](options.message);
}
export async function showIgnorableNotification(message: string, fn: () => Promise<any>): Promise<void> {
const ignoreNotification = workspace.getConfiguration('azureBlockchainService').get('ignoreLongRunningTaskNotification');
await window.withProgress({
location: ProgressLocation.Window,
title: message,
}, async () => {
if (ignoreNotification) {
await fn();
} else {
await window.withProgress({
location: ProgressLocation.Notification,
title: message,
}, fn);
}
});
}
namespace Notification {
export const types = {
error: window.showErrorMessage,
info: window.showInformationMessage,
warning: window.showWarningMessage,
};
export interface IShowNotificationOptions {
type?: NotificationOptions.error | NotificationOptions.warning | NotificationOptions.info;
message: string;
}
}

Просмотреть файл

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export interface IExtensionAdapter {
validateExtension: () => Promise<void>;
build: () => Promise<void>;
deploy: () => Promise<void>;
}

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as semver from 'semver';
import { commands, Extension, extensions } from 'vscode';
import { Constants } from '../../Constants';
import { IExtensionAdapter } from './IExtensionAdapter';
export class OpenZeppelinExtensionAdapter implements IExtensionAdapter {
private extensionInfo: any;
private extension: Extension<any> | undefined;
constructor() {
this.extensionInfo = Constants.externalExtensions[Constants.coreSdk.openZeppelin];
this.extension = extensions.getExtension(this.extensionInfo.name);
}
public async validateExtension(): Promise<void> {
if (!this.extension) {
throw new Error(Constants.errorMessageStrings.ExtensionNotInstalled(this.extensionInfo.name));
}
const version = this.extension.packageJSON.version;
if (!semver.eq(version, this.extensionInfo.supportedVersion)) {
throw new Error(Constants.informationMessage.unsupportedVersionOfExternalExtension(
this.extensionInfo.name, version, this.extensionInfo.supportedVersion));
}
if (!this.extension.isActive) {
await this.extension.activate();
}
}
public async build(): Promise<void> {
return commands.executeCommand(this.extensionInfo.commands.buildContracts);
}
public async deploy(): Promise<void> {
return commands.executeCommand(this.extensionInfo.commands.deployContracts);
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { TruffleCommands } from '../../commands/TruffleCommands';
import { IExtensionAdapter } from './IExtensionAdapter';
export class TruffleExtensionAdapter implements IExtensionAdapter {
// tslint:disable-next-line:no-empty
public async validateExtension(): Promise<void> {}
public async build(): Promise<void> {
return TruffleCommands.buildContracts();
}
public async deploy() {
return TruffleCommands.deployContracts();
}
}

Просмотреть файл

@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export * from './IExtensionAdapter';
export * from './TruffleExtensionAdapter';
export * from './OpenZeppelinExtensionAdapter';

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,227 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as fs from 'fs-extra';
import * as sinon from 'sinon';
import uuid = require('uuid');
import { generateFlowAppForMicroservice, generateLogicAppForMicroservice } from '../../src/Generators/CodeGenerator/CodeGenerator';
describe('CodeGenerator', () => {
afterEach(() => {
sinon.restore();
});
it('generateLogicAppForMicroservice should return logic app data for microservice.', async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(contractWithDifferentTypeOfVariableAndFunction);
// Act
const logicAppData = generateLogicAppForMicroservice('abi', 'contract address', 'subscriptionId', 'location', '');
// Assert
assert.strictEqual(
Object.keys((logicAppData.definition as any).actions.Switch.cases).length,
12,
'logicAppData should have special count of cases.');
});
it('generateFlowAppForMicroservice should return flow app data for microservice.', async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(contractWithDifferentTypeOfVariableAndFunction);
const testContractName = uuid.v4().toString();
// Act
const flowAppData = generateFlowAppForMicroservice(testContractName, '[]', 'contract address', '');
const clientData = JSON.parse(flowAppData.clientdata);
// Assert
assert.strictEqual(
Object.keys(clientData.properties.definition.actions.Switch.cases).length,
12,
'flowAppData should have special count of cases');
assert.strictEqual(flowAppData.name, testContractName, 'Name should equals parameter which we send to function.');
});
it('generateFlowAppForMicroservice should return flow app data with special operationId for functions.',
async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(contractWithDifferentAccessOfFunctions);
const testContractName = uuid.v4().toString();
// Act
const flowAppData = generateFlowAppForMicroservice(testContractName, '[]', 'contract address', '');
// Assert
const cases = JSON.parse(flowAppData.clientdata).properties.definition.actions.Switch.cases;
const casesPropertyNames = Object.getOwnPropertyNames(cases);
const simpleFunctionNames = casesPropertyNames.filter((name) => !name.includes('View') && !name.includes('Pure'));
const viewPureFunctionNames = casesPropertyNames.filter((name) => name.includes('View') || name.includes('Pure'));
const viewPureFunctionNamesWithParameters = viewPureFunctionNames.filter((name) => !name.includes('NOTParam'));
const viewPureFunctionNamesWithoutParameters = viewPureFunctionNames.filter((name) => name.includes('NOTParam'));
const functionNamesWithReturnValue = casesPropertyNames.filter((name) => !name.includes('NOTReturn'));
const functionNamesWithoutReturnValue = casesPropertyNames.filter((name) => name.includes('NOTReturn'));
viewPureFunctionNamesWithParameters.forEach((name) => {
const functionName = name.replace(/Case_/, '');
assert.strictEqual(
cases[name].actions[functionName].inputs.host.operationId,
'ExecuteSmartContractFunction',
'Case should have operationId equals `ExecuteSmartContractFunction`, when function has parameters and modifier access equal `view` or `pure`');
});
viewPureFunctionNamesWithoutParameters.forEach((name) => {
const functionName = name.replace(/Case_/, '');
assert.strictEqual(
cases[name].actions[functionName].inputs.host.operationId,
'GetSmartContractProperties',
'Case should have operationId equals `GetSmartContractProperties`, when function without parameters has modifier access equal `view` or `pure`');
});
simpleFunctionNames.forEach((name) => {
const functionName = name.replace(/Case_/, '');
assert.strictEqual(
cases[name].actions[functionName].inputs.host.operationId,
'ExecuteContractFunction',
'Case should have operationId equals `ExecuteContractFunction`, when function does not have modifier access equal `view` or `pure`');
});
functionNamesWithReturnValue.forEach((name) => {
const functionName = name.replace(/Case_/, '');
assert.strictEqual(
cases[name].actions[`${functionName}Response`].inputs.body,
`@outputs('${functionName}')?['body/Function Output']`,
'Case should have response with body, which return Function Output, when function has return value');
});
functionNamesWithoutReturnValue.forEach((name) => {
const functionName = name.replace(/Case_/, '');
assert.strictEqual(
cases[name].actions[`${functionName}Response`].inputs.body,
`@body('${functionName}')`,
'Case should have response with body, which return function body, when function does not have return value');
});
});
});
const contractWithDifferentTypeOfVariableAndFunction = `pragma solidity ^0.5.0;
contract TestContract
{
enum StateType { Request, Respond }
StateType public State;
address public Responder;
string public RequestMessage;
event StateChanged(string stateData);
uint public publicVariable;
uint private privateVariable;
uint internal internalVariable;
constructor(string memory message) public
{
RequestMessage = message;
State = StateType.Request;
emit StateChanged('Request');
}
function public1(uint a) public { a + 1; }
function public2(uint a) public returns(bool) { a + 1; return true; }
function public3(uint a) public pure { a + 1; }
function public4(uint a) public view { a + 1; }
function public5(uint a) public payable { a + 1; }
function private1(uint a) private { a + 1; }
function private2(uint a) private pure { a + 1; }
function private3(uint a) private view { a + 1; }
function internal1(uint a) internal { a + 1; }
function internal2(uint a) internal pure { a + 1; }
function internal3(uint a) internal view { a + 1; }
function ext1(uint a) external { a + 1; }
function ext2(uint a) external pure { a + 1; }
function ext3(uint a) external view {a + 1; }
}`;
const contractWithDifferentAccessOfFunctions = `pragma solidity ^0.5.0;
contract TestContract {
constructor() public {}
function functionNOTParamNOTReturn() public {}
function functionNOTParamNOTReturnView() public view {}
function functionNOTParamNOTReturnPure() public pure {}
function functionNOTParamNOTReturnPayable() public payable {}
function functionParamNOTReturn(uint a) public { a + 1; }
function functionParamNOTReturnView(uint a) public view { a + 1; }
function functionParamNOTReturnPure(uint a) public pure { a + 1; }
function functionParamNOTReturnPayable(uint a) public payable { a + 1; }
function functionNOTParamReturn() public returns (uint) { return 1; }
function functionNOTParamReturnView() public view returns (uint) { return 1; }
function functionNOTParamReturnPure() public pure returns (uint) { return 1; }
function functionNOTParamReturnPayable() public payable returns (uint) { return 1; }
function functionParamReturn(uint input) public returns (uint) { return input; }
function functionParamReturnView(uint input) public view returns (uint) { return input; }
function functionParamReturnPure(uint input) public pure returns (uint) { return input; }
function functionParamReturnPayable(uint input) public payable returns (uint) { return input; }
function externalfunctionNOTParamNOTReturn() external {}
function externalfunctionNOTParamNOTReturnView() external view {}
function externalfunctionNOTParamNOTReturnPure() external pure {}
function externalfunctionNOTParamNOTReturnPayable() external payable {}
function externalfunctionParamNOTReturn(uint a) external { a + 1; }
function externalfunctionParamNOTReturnView(uint a) external view { a + 1; }
function externalfunctionParamNOTReturnPure(uint a) external pure { a + 1; }
function externalfunctionParamNOTReturnPayable(uint a) external payable { a + 1; }
function externalfunctionNOTParamReturn() external returns (uint) { return 1; }
function externalfunctionNOTParamReturnView() external view returns (uint) { return 1; }
function externalfunctionNOTParamReturnPure() external pure returns (uint) { return 1; }
function externalfunctionNOTParamReturnPayable() external payable returns (uint) { return 1; }
function externalfunctionParamReturn(uint input) external returns (uint) { return input; }
function externalfunctionParamReturnView(uint input) external view returns (uint) { return input; }
function externalfunctionParamReturnPure(uint input) external pure returns (uint) { return input; }
function externalfunctionParamReturnPayable(uint input) external payable returns (uint) { return input; }
}`;

Просмотреть файл

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as fs from 'fs-extra';
import * as sinon from 'sinon';
import { parseSolidityContract } from '../../src/Generators/CodeGenerator/ContractParser';
describe('ContractParser', () => {
afterEach(() => {
sinon.restore();
});
it('parser should return necessary functions and variables.', async () => {
// Arrange
sinon.stub(fs, 'readFileSync').returns(contractMetadata);
// Act
const { variables, functionsDefinitions } = parseSolidityContract('');
// Assert
assert.strictEqual(functionsDefinitions.length, 8, 'Array should have only public and external functions');
assert.strictEqual(variables.length, 4, 'Array should have only public variables');
});
});
const contractMetadata = `pragma solidity ^0.5.0;
contract TestContract
{
enum StateType { Request, Respond }
StateType public State;
address public Responder;
string public RequestMessage;
event StateChanged(string stateData);
uint public publicVariable;
uint private privateVariable;
uint internal internalVariable;
// constructor function
constructor(string memory message) public
{
RequestMessage = message;
State = StateType.Request;
emit StateChanged('Request');
}
function public1(uint a) public { a + 1; }
function public2(uint a) public returns(bool) { a + 1; return true; }
function public3(uint a) public pure { a + 1; }
function public4(uint a) public view { a + 1; }
function public5(uint a) public payable { a + 1; }
function private1(uint a) private { a + 1; }
function private2(uint a) private pure { a + 1; }
function private3(uint a) private view { a + 1; }
function internal1(uint a) internal { a + 1; }
function internal2(uint a) internal pure { a + 1; }
function internal3(uint a) internal view { a + 1; }
function ext1(uint a) external { a + 1; }
function ext2(uint a) external pure { a + 1; }
function ext3(uint a) external view {a + 1; }
}`;

Просмотреть файл

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as semver from 'semver';
import * as sinon from 'sinon';
import * as uuid from 'uuid';
import { commands, extensions } from 'vscode';
import { OpenZeppelinExtensionAdapter } from '../src/services/extensionAdapter';
describe('sdkService', () => {
let executeCommandMock: sinon.SinonStub<any>;
let getExtensionMock: sinon.SinonStub<any>;
let eqMock: sinon.SinonStub<any>;
let openZeppelinExtensionAdapter: OpenZeppelinExtensionAdapter;
beforeEach(() => {
executeCommandMock = sinon.stub(commands, 'executeCommand');
getExtensionMock = sinon.stub(extensions, 'getExtension');
eqMock = sinon.stub(semver, 'eq');
});
afterEach(() => {
sinon.restore();
});
describe('validateExtension', () => {
it('should throw exception when extension not found', async () => {
// Arrange
getExtensionMock.returns(undefined);
openZeppelinExtensionAdapter = new OpenZeppelinExtensionAdapter();
// Act and assert
assert.rejects(openZeppelinExtensionAdapter.validateExtension());
});
it('should throw exception when extension version not supported', async () => {
// Arrange
getExtensionMock.returns({
isActive: true,
packageJSON: {
value: uuid.v4(),
},
});
eqMock.returns(false);
openZeppelinExtensionAdapter = new OpenZeppelinExtensionAdapter();
// Act and assert
assert.rejects(openZeppelinExtensionAdapter.validateExtension());
});
});
describe('build', () => {
it('should execute extension command', async () => {
// Arrange
getExtensionMock.returns({
isActive: true,
packageJSON: {
value: uuid.v4(),
},
});
openZeppelinExtensionAdapter = new OpenZeppelinExtensionAdapter();
// Act
await openZeppelinExtensionAdapter.build();
// Assert
assert.strictEqual(
executeCommandMock.calledOnce,
true,
'executeCommand should called once',
);
});
});
describe('deploy', () => {
it('should execute extension command', async () => {
// Arrange
getExtensionMock.returns({
isActive: true,
packageJSON: {
value: uuid.v4(),
},
});
openZeppelinExtensionAdapter = new OpenZeppelinExtensionAdapter();
// Act
await openZeppelinExtensionAdapter.deploy();
// Assert
assert.strictEqual(
executeCommandMock.calledOnce,
true,
'executeCommand should called once',
);
});
});
});

Просмотреть файл

@ -8,7 +8,7 @@ import rewire = require('rewire');
import * as sinon from 'sinon';
import uuid = require('uuid');
import * as vscode from 'vscode';
import { TruffleCommands } from '../../src/commands';
import { TruffleCommands } from '../../src/commands/TruffleCommands';
import { Constants } from '../../src/Constants';
import * as helpers from '../../src/helpers';
import { openZeppelinHelper, TruffleConfiguration } from '../../src/helpers';

Просмотреть файл

@ -0,0 +1,746 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as fs from 'fs';
import * as path from 'path';
import rewire = require('rewire');
import * as sinon from 'sinon';
import uuid = require('uuid');
import * as vscode from 'vscode';
import { TruffleCommands } from '../../src/commands';
import { Constants } from '../../src/Constants';
import * as helpers from '../../src/helpers';
import { openZeppelinHelper, TruffleConfiguration } from '../../src/helpers';
import * as commands from '../../src/helpers/command';
import { CancellationEvent, ItemType } from '../../src/Models';
import {
AzureBlockchainNetworkNode,
AzureBlockchainProject,
AzureBlockchainService,
BlockchainDataManagerNetworkNode,
BlockchainDataManagerProject,
BlockchainDataManagerService,
IExtensionItem,
InfuraNetworkNode,
InfuraProject,
InfuraService,
LocalNetworkNode,
LocalProject,
LocalService,
Member,
Service,
} from '../../src/Models/TreeItems';
import { ConsortiumResourceExplorer } from '../../src/resourceExplorers';
import { GanacheService, MnemonicRepository, OpenZeppelinMigrationsService, TreeManager } from '../../src/services';
import { OpenZeppelinService } from '../../src/services';
import { OZContractValidated } from '../../src/services/openZeppelin/models';
import { TestConstants } from '../TestConstants';
import { AzureAccountHelper } from '../testHelpers/AzureAccountHelper';
const { service } = Constants.treeItemData;
describe('TruffleCommands', () => {
describe('Integration test', async () => {
describe('deployContracts', () => {
let requiredMock: sinon.SinonMock;
let checkAppsSilentMock: sinon.SinonExpectation;
let installTruffleMock: sinon.SinonExpectation;
let isHdWalletProviderRequiredMock: sinon.SinonExpectation;
let checkHdWalletProviderVersionMock: sinon.SinonExpectation;
let installTruffleHdWalletProviderMock: sinon.SinonExpectation;
let getWorkspaceRootMock: any;
let windowMock: sinon.SinonMock;
let showQuickPickMock: any;
let showInputBoxMock: any;
let showSaveDialogMock: sinon.SinonExpectation;
let showInformationMessageMock: any;
let ganacheServiceMock: sinon.SinonMock;
let startGanacheServerMock: sinon.SinonExpectation;
let getItemsMock: sinon.SinonStub<[], IExtensionItem[]>;
let loadStateMock: sinon.SinonStub<[], IExtensionItem[]>;
let servicesItems: Service[];
let truffleConfigSetNetworkMock: any;
let truffleConfigGetNetworkMock: any;
let truffleConfigGenerateMnemonicMock: any;
let commandContextMock: sinon.SinonMock;
let executeCommandMock: sinon.SinonExpectation;
let mnemonicRepositoryMock: sinon.SinonMock;
let getMnemonicMock: sinon.SinonStub<any[], any>;
let getAllMnemonicPathsMock: sinon.SinonStub<any[], any>;
let saveMnemonicPathMock: sinon.SinonExpectation;
let writeFileSyncMock: any;
let getAccessKeysMock: any;
let getExtensionMock: any;
let openZeppelinvalidateContractsAsyncMock: any;
let projectJsonExistsStub: sinon.SinonStub<[], boolean>;
let shouldUpgradeOpenZeppelinAsyncStub: sinon.SinonStub<[], Promise<boolean>>;
beforeEach(async () => {
sinon.stub(helpers.openZeppelinHelper, 'tryGetCurrentOpenZeppelinVersionAsync');
sinon.stub(helpers.openZeppelinHelper, 'defineContractRequiredParameters');
sinon.stub(OpenZeppelinMigrationsService, 'generateMigrations');
getWorkspaceRootMock = sinon.stub(helpers, 'getWorkspaceRoot');
requiredMock = sinon.mock(helpers.required);
checkAppsSilentMock = requiredMock.expects('checkAppsSilent');
installTruffleMock = requiredMock.expects('installTruffle');
isHdWalletProviderRequiredMock = requiredMock.expects('isHdWalletProviderRequired');
checkHdWalletProviderVersionMock = requiredMock.expects('checkHdWalletProviderVersion');
installTruffleHdWalletProviderMock = requiredMock.expects('installTruffleHdWalletProvider');
isHdWalletProviderRequiredMock.returns(false);
checkHdWalletProviderVersionMock.returns(false);
windowMock = sinon.mock(vscode.window);
showQuickPickMock = sinon.stub(vscode.window, 'showQuickPick');
showInputBoxMock = sinon.stub(vscode.window, 'showInputBox');
showSaveDialogMock = windowMock.expects('showSaveDialog');
sinon.stub(vscode.window, 'showErrorMessage');
showInformationMessageMock = sinon.stub(vscode.window, 'showInformationMessage');
ganacheServiceMock = sinon.mock(GanacheService);
startGanacheServerMock = ganacheServiceMock.expects('startGanacheServer');
getItemsMock = sinon.stub(TreeManager, 'getItems');
loadStateMock = sinon.stub(TreeManager, 'loadState');
servicesItems = await createTestServicesItems();
getItemsMock.returns(servicesItems);
loadStateMock.returns(servicesItems);
truffleConfigSetNetworkMock = sinon.stub(TruffleConfiguration.TruffleConfig.prototype, 'setNetworks');
truffleConfigGetNetworkMock = sinon.stub(TruffleConfiguration.TruffleConfig.prototype, 'getNetworks');
truffleConfigGetNetworkMock.returns(getTestTruffleNetworks());
truffleConfigGenerateMnemonicMock = sinon.stub(TruffleConfiguration, 'generateMnemonic');
truffleConfigGenerateMnemonicMock.returns(TestConstants.testMnemonic);
commandContextMock = sinon.mock(commands);
executeCommandMock = commandContextMock.expects('executeCommand');
mnemonicRepositoryMock = sinon.mock(MnemonicRepository);
getMnemonicMock = mnemonicRepositoryMock.expects('getMnemonic').returns(TestConstants.testMnemonic);
getAllMnemonicPathsMock = mnemonicRepositoryMock.expects('getAllMnemonicPaths').returns([] as string []);
saveMnemonicPathMock = mnemonicRepositoryMock.expects('saveMnemonicPath');
writeFileSyncMock = sinon.stub(fs, 'writeFileSync');
getAccessKeysMock = sinon.stub(ConsortiumResourceExplorer.prototype, 'getAccessKeys');
getExtensionMock = sinon.stub(vscode.extensions, 'getExtension').returns(AzureAccountHelper.mockExtension);
projectJsonExistsStub = sinon.stub(OpenZeppelinService, 'projectJsonExists').returns(false);
shouldUpgradeOpenZeppelinAsyncStub = sinon.stub(openZeppelinHelper, 'shouldUpgradeOpenZeppelinAsync')
.resolves(false);
const openZeppelinServiceMock = sinon.mock(OpenZeppelinService);
openZeppelinvalidateContractsAsyncMock = openZeppelinServiceMock.expects('validateContractsAsync')
.resolves([]);
});
afterEach(() => {
sinon.restore();
});
it('should throw exception when config file not found', async () => {
// Arrange
getWorkspaceRootMock.returns(__dirname);
executeCommandMock.returns(uuid.v4());
// Act and assert
await assert.rejects(TruffleCommands.deployContracts(),
Error,
Constants.errorMessageStrings.TruffleConfigIsNotExist);
});
it('should throw cancellationEvent when showQuickPick return undefined', async () => {
// Arrange
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
showQuickPickMock.returns(undefined);
// Act and assert
await assert.rejects(TruffleCommands.deployContracts(), CancellationEvent);
});
it('should install TruffleHdWalletProvider when it required', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
isHdWalletProviderRequiredMock.returns(true);
checkHdWalletProviderVersionMock.returns(false);
executeCommandMock.returns(uuid.v4());
showInformationMessageMock.returns(Constants.informationMessage.installButton);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);
});
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(
isHdWalletProviderRequiredMock.calledOnce,
true,
'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
true,
'checkHdWalletProviderVersion should be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
true,
'installTruffleHdWalletProvider should be called');
});
it('should not install TruffleHdWalletProvider when it version correct', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
isHdWalletProviderRequiredMock.returns(true);
checkHdWalletProviderVersionMock.returns(true);
executeCommandMock.returns(uuid.v4());
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);
});
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(isHdWalletProviderRequiredMock.calledOnce, true, 'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
true,
'checkHdWalletProviderVersion should be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to development should complete successfully with updating OpenZeppelin', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
isHdWalletProviderRequiredMock.returns(false);
projectJsonExistsStub.returns(true);
shouldUpgradeOpenZeppelinAsyncStub.resolves(true);
const updateOpenZeppelinInUserSettingsSpy =
sinon.stub(helpers.openZeppelinHelper, 'upgradeOpenZeppelinUserSettingsAsync').resolves();
const updateOpenZeppelinContractsSpy =
sinon.stub(helpers.openZeppelinHelper, 'upgradeOpenZeppelinContractsAsync').resolves();
const truffleCommandsRewire = rewire('../../src/commands/TruffleCommands');
truffleCommandsRewire.__set__('validateOpenZeppelinContracts', sinon.mock());
const validateOpenZeppelinContractsMock = truffleCommandsRewire.__get__('validateOpenZeppelinContracts');
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);
});
// Act
await truffleCommandsRewire.TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(isHdWalletProviderRequiredMock.calledOnce, true, 'isHdWalletProviderRequired should be called');
assert.strictEqual(updateOpenZeppelinInUserSettingsSpy.called, true, 'updateOpenZeppelinInUserSettings should be called');
assert.strictEqual(updateOpenZeppelinContractsSpy.called, true, 'updateOpenZeppelinContracts should be called');
assert.strictEqual(validateOpenZeppelinContractsMock.calledOnce, true, 'validateOpenZeppelinContracts should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to development should throw exception when there is an error on command execution', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.throws(TestConstants.testError);
showQuickPickMock.callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.development);
});
// Act and assert
await assert.rejects(TruffleCommands.deployContracts(), Error);
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(isHdWalletProviderRequiredMock.calledOnce, true, 'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to network should complete successfully', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.testNetwork);
});
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, false, 'startGanacheServer should not be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(isHdWalletProviderRequiredMock.calledOnce, true, 'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to network should throw exception when there is an error on command execution', async () => {
// Arrange
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.throws(TestConstants.testError);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.testNetwork);
});
// Act and assert
await assert.rejects(TruffleCommands.deployContracts());
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, false, 'startGanacheServer should not be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, false, 'truffleConfig.setNetwork should not be called');
assert.strictEqual(isHdWalletProviderRequiredMock.calledOnce, true, 'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to local network should complete successfully', async () => {
// Arrange
const { local } = TestConstants.consortiumTestNames;
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
const networkNodeName = getDeployName(service.local.prefix, local, local);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === networkNodeName);
});
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, true, 'truffleConfig.setNetwork should be called');
assert.strictEqual(
isHdWalletProviderRequiredMock.calledOnce,
true,
'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to local network should throw exception when there is an error on command execution', async () => {
// Arrange
const { local } = TestConstants.consortiumTestNames;
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.throws(TestConstants.testError);
const networkNodeName = getDeployName(service.local.prefix, local, local);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === networkNodeName);
});
// Act and assert
await assert.rejects(TruffleCommands.deployContracts());
assert.strictEqual(showQuickPickMock.calledOnce, true, 'showQuickPick should be called once');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, true, 'startGanacheServer should be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, true, 'truffleConfig.setNetwork should be called');
assert.strictEqual(
isHdWalletProviderRequiredMock.calledOnce,
true,
'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to AzureBlockchainService should generate mnemonic and complete successfully', async () => {
// Arrange
const { consortium, member, transactionNode } = azureNames;
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
getAccessKeysMock.returns(uuid.v4());
const networkNodeName = getDeployName(service.azure.prefix, consortium, transactionNode, [member]);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === networkNodeName);
});
showQuickPickMock.onCall(1).callsFake((items: any) => {
return items.find((item: any) => item.label === Constants.placeholders.generateMnemonic);
});
showSaveDialogMock.returns(uuid.v4());
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.called, true, 'showQuickPick should be called');
assert.strictEqual(showQuickPickMock.callCount, 2, 'showQuickPick should be called twice');
assert.strictEqual(getAccessKeysMock.called, true, 'getAccessKeys should be called');
assert.strictEqual(showInputBoxMock.called, false, 'showInputBox should not be called');
assert.strictEqual(getMnemonicMock.called, false, 'getMnemonic should not be called');
assert.strictEqual(getAllMnemonicPathsMock.called, true, 'getAllMnemonicPaths should be called');
assert.strictEqual(saveMnemonicPathMock.called, true, 'saveMnemonicPath should be called');
assert.strictEqual(writeFileSyncMock.called, true, 'writeFileSync should be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, false, 'startGanacheServer should not be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, true, 'truffleConfig.setNetwork should be called');
assert.strictEqual(getExtensionMock.called, true, 'getExtension should be called');
assert.strictEqual(
isHdWalletProviderRequiredMock.calledOnce,
true,
'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('to AzureBlockchainService should complete successfully when user paste mnemonic', async () => {
// Arrange
const { consortium, member, transactionNode } = azureNames;
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
getAccessKeysMock.returns(uuid.v4());
const networkNodeName = getDeployName(service.azure.prefix, consortium, transactionNode, [member]);
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === networkNodeName);
});
showQuickPickMock.onCall(1).callsFake((items: any) => {
return items.find((item: any) => item.label === Constants.placeholders.pasteMnemonic);
});
showInputBoxMock.onCall(0).returns(TestConstants.testMnemonic);
showSaveDialogMock.returns(uuid.v4());
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(showQuickPickMock.called, true, 'showQuickPick should be called');
assert.strictEqual(showQuickPickMock.callCount, 2, 'showQuickPick should be called twice');
assert.strictEqual(getAccessKeysMock.called, true, 'getAccessKeys should be called');
assert.strictEqual(showInputBoxMock.calledOnce, true, 'showInputBox should be called once');
assert.strictEqual(getMnemonicMock.called, false, 'getMnemonic should not be called');
assert.strictEqual(getAllMnemonicPathsMock.called, true, 'getAllMnemonicPaths should be called');
assert.strictEqual(saveMnemonicPathMock.called, true, 'saveMnemonicPath should be called');
assert.strictEqual(writeFileSyncMock.called, true, 'writeFileSync should be called');
assert.strictEqual(checkAppsSilentMock.calledOnce, true, 'checkAppsSilent should be called once');
assert.strictEqual(installTruffleMock.called, false, 'installTruffle should not be called');
assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called');
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
assert.strictEqual(startGanacheServerMock.called, false, 'startGanacheServer should not be called');
assert.strictEqual(truffleConfigSetNetworkMock.called, true, 'truffleConfig.setNetwork should be called');
assert.strictEqual(getExtensionMock.called, true, 'getExtension should be called');
assert.strictEqual(
isHdWalletProviderRequiredMock.calledOnce,
true,
'isHdWalletProviderRequired should be called');
assert.strictEqual(
checkHdWalletProviderVersionMock.calledOnce,
false,
'checkHdWalletProviderVersion should not be called');
assert.strictEqual(
installTruffleHdWalletProviderMock.calledOnce,
false,
'installTruffleHdWalletProvider should not be called');
});
it('Blockchain Data Manager should be ignored in deploy destination list', async () => {
// Arrange
let isBDMExist = false;
const { local } = TestConstants.consortiumTestNames;
checkAppsSilentMock.returns(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
executeCommandMock.returns(uuid.v4());
const networkNodeName = getDeployName(service.local.prefix, local, local);
showQuickPickMock.onCall(0).callsFake((items: any) => {
isBDMExist = items.some((item: any) => item.detail === Constants.treeItemData.service.bdm.label);
return items.find((item: any) => item.label === networkNodeName);
});
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(isBDMExist, false, 'deploy destination list should not have Blockchain Data Manager');
});
describe('validating openZeppelin contracts before deploy', () => {
beforeEach(() => {
checkAppsSilentMock.resolves(true);
getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder));
showInputBoxMock.returns(Constants.confirmationDialogResult.yes);
executeCommandMock.returns(uuid.v4());
showQuickPickMock.onCall(0).callsFake((items: any) => {
return items.find((item: any) => item.label === TestConstants.servicesNames.testNetwork);
});
});
afterEach(() => {
sinon.restore();
});
it('should pass when openZeppelin contracts are valid', async () => {
// Arrange
projectJsonExistsStub.returns(true);
openZeppelinvalidateContractsAsyncMock.resolves([ new OZContractValidated('1', true, true) ]);
// Act
await TruffleCommands.deployContracts();
// Assert
assert.strictEqual(executeCommandMock.called, true, 'executeCommand should be called');
});
it('should throw error when downloaded openZeppelin contract has invalid hash', async () => {
// Arrange
projectJsonExistsStub.returns(true);
openZeppelinvalidateContractsAsyncMock.resolves([ new OZContractValidated('1', true, false) ]);
// Act and Assert
await assert.rejects(TruffleCommands.deployContracts(), Error);
});
it('should throw error when downloaded openZeppelin contract doesn\'t exist on the disk', async () => {
// Arrange
projectJsonExistsStub.returns(true);
openZeppelinvalidateContractsAsyncMock.resolves([ new OZContractValidated('1', false) ]);
// Act and Assert
await assert.rejects(TruffleCommands.deployContracts(), Error);
});
it('should throw error when any downloaded openZeppelin contract is invalid', async () => {
// Arrange
projectJsonExistsStub.returns(true);
openZeppelinvalidateContractsAsyncMock.resolves([
new OZContractValidated('1', true, false),
new OZContractValidated('2', true, true),
]);
// Act and Assert
await assert.rejects(TruffleCommands.deployContracts(), Error);
});
});
});
});
});
const azureNames = {
consortium: uuid.v4(),
member: uuid.v4(),
transactionNode: TestConstants.servicesNames.testConsortium,
};
async function createTestServicesItems(): Promise<Service[]> {
const services: Service[] = [];
const azureBlockchainService = new AzureBlockchainService();
const localService = new LocalService();
const infuraService = new InfuraService();
const bdmService = new BlockchainDataManagerService();
const azureBlockchainProject = new AzureBlockchainProject(
azureNames.consortium,
uuid.v4(),
uuid.v4(),
[azureNames.member],
);
const member = new Member(azureNames.member);
const transactionNode
= new AzureBlockchainNetworkNode(azureNames.transactionNode, uuid.v4(), '*', '', '', azureNames.member);
member.addChild(transactionNode);
azureBlockchainProject.addChild(member);
const defaultPort = 8545;
const defaultLabel = TestConstants.consortiumTestNames.local;
const localProject = new LocalProject(defaultLabel, defaultPort);
const defaultUrl = `${Constants.networkProtocols.http}${Constants.localhost}:${defaultPort}`;
const localNetworkNode = new LocalNetworkNode(defaultLabel, defaultUrl, '*');
localProject.addChild(localNetworkNode);
const infuraProject = new InfuraProject(uuid.v4(), uuid.v4());
const infuraNetworkNode = new InfuraNetworkNode(uuid.v4(), uuid.v4(), uuid.v4());
infuraProject.addChild(infuraNetworkNode);
const bdmProject = new BlockchainDataManagerProject(uuid.v4(), uuid.v4(), uuid.v4());
const bdmNetworkNode = new BlockchainDataManagerNetworkNode(
uuid.v4(),
'*',
uuid.v4(),
uuid.v4(),
[],
ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION,
uuid.v4());
bdmProject.addChild(bdmNetworkNode);
azureBlockchainService.addChild(azureBlockchainProject);
localService.addChild(localProject);
infuraService.addChild(infuraProject);
bdmService.addChild(bdmProject);
services.push(azureBlockchainService, localService, infuraService, bdmService);
return services;
}
function getTestTruffleNetworks(): TruffleConfiguration.INetwork[] {
const networks: TruffleConfiguration.INetwork[] = [];
networks.push({
name: TestConstants.servicesNames.development,
options: {
host: '127.0.0.1',
network_id: '*',
port: 8545,
},
},
{
name: TestConstants.servicesNames.testNetwork,
options: {
gas: 4712388,
gasPrice: 100000000000,
network_id: 2,
},
});
return networks;
}
function getDeployName(prefix: string, projectName: string, nodeName: string, args?: string[]): string {
if (args) {
return [prefix, projectName, ...args, nodeName].join('_');
}
return [prefix, projectName, nodeName].join('_');
}

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as sinon from 'sinon';
import { TruffleCommands } from '../src/commands/TruffleCommands';
import { TruffleExtensionAdapter } from '../src/services/extensionAdapter';
describe('TruffleExtensionAdapter', () => {
let buildContractsMock: sinon.SinonStub<any>;
let deployContractsMock: sinon.SinonStub<any>;
let truffleExtensionAdapter: TruffleExtensionAdapter;
beforeEach(() => {
buildContractsMock = sinon.stub(TruffleCommands, 'buildContracts');
deployContractsMock = sinon.stub(TruffleCommands, 'deployContracts');
truffleExtensionAdapter = new TruffleExtensionAdapter();
});
afterEach(() => {
sinon.restore();
});
it('build method should call truffleCommands.buildContracts', async () => {
// Act
await truffleExtensionAdapter.build();
// Assert
assert.strictEqual(
buildContractsMock.calledOnce,
true,
'TruffleCommands.buildContracts should be called once',
);
});
it('deploy method should call truffleCommands.buildContracts', async () => {
// Act
await truffleExtensionAdapter.deploy();
// Assert
assert.strictEqual(
deployContractsMock.calledOnce,
true,
'TruffleCommands.deployContracts should be called once',
);
});
});

Просмотреть файл

@ -179,7 +179,7 @@ describe('OpenZeppelinCommands tests', () => {
// Arrange
const showQuickPick = sinon.stub(helpers, 'showQuickPick');
showQuickPick.callsFake(async (...args: any[]) => {
return args[0];
return args[0].find((arg: any) => arg.label === selectedCategory.name);
});
// Act

Просмотреть файл

@ -0,0 +1,362 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as assert from 'assert';
import * as path from 'path';
import rewire = require('rewire');
import * as sinon from 'sinon';
import uuid = require('uuid');
import {
CancellationToken,
MessageItem,
MessageOptions,
Progress,
ProgressOptions,
QuickPickItem,
QuickPickOptions,
window,
} from 'vscode';
import { Constants } from '../../src/Constants';
import * as helpers from '../../src/helpers';
import { openZeppelinHelper } from '../../src/helpers';
import { TruffleConfiguration } from '../../src/helpers/truffleConfig';
import { CancellationEvent } from '../../src/Models';
import {
OpenZeppelinMigrationsService,
OpenZeppelinService,
} from '../../src/services';
import {
IDownloadingResult,
IOZAsset,
IOZContractCategory,
OZAssetType,
PromiseState,
} from '../../src/services/openZeppelin/models';
import { OpenZeppelinManifest } from '../../src/services/openZeppelin/OpenZeppelinManifest';
describe('OpenZeppelinCommands tests', () => {
let testCategories: IOZContractCategory[];
let getCategoriesStub: sinon.SinonSpy<[], IOZContractCategory[]>;
let collectAssetsWithDependenciesStub: sinon.SinonSpy<[(string[] | undefined)?], IOZAsset[]>;
let downloadAssetsAsyncStub: sinon.SinonStub<
[string, IOZAsset[], (boolean | undefined)?, (string | undefined)?],
Promise<IDownloadingResult[]>>;
let getAssetsStatusStub: sinon.SinonStub<any>;
let generateMigrationsStub: sinon.SinonStub<[IOZAsset[]], Promise<void>>;
let getCategoryApiDocumentationUrlStub: sinon.SinonStub<any>;
let updateProjectJsonAsyncStub: sinon.SinonStub<[string, IOZContractCategory, IOZAsset[]], Promise<void>>;
let withProgressStub: sinon.SinonStub<[ProgressOptions,
(progress: Progress<any>, token: CancellationToken) => any], any>;
let showQuickPickStub: sinon.SinonStub<
[QuickPickItem[] | Thenable<QuickPickItem[]>, (QuickPickOptions | undefined)?, (CancellationToken | undefined)?],
any>;
let showInformationMessageStub: sinon.SinonStub<[string, MessageOptions, ...MessageItem[]], any>;
let showErrorMessageStub: sinon.SinonStub<[string, MessageOptions, ...MessageItem[]], any>;
let selectedCategory: IOZContractCategory;
let testAssets: IOZAsset[];
let openZeppelinCommandsRewire: any;
let openStub: sinon.SinonStub<any[], any>;
beforeEach(() => {
const numberOfCategory = 2;
testCategories = getTestCategories();
selectedCategory = testCategories[numberOfCategory];
testAssets = getTestAssetsWithDependencies(selectedCategory.assets);
const mockOpenZeppelinManifest = {
collectAssetsWithDependencies: (_assetIds: string[]) => testAssets,
getAssets: () => [] as IOZAsset[],
getBaseUrlToContractsSource: () => 'http://test.com',
getCategories: () => testCategories,
getCategoryApiDocumentationUrl: (_category: IOZContractCategory) => '',
getVersion: () => '1.0.0',
} as OpenZeppelinManifest;
const getWorkspaceRootMock = sinon.stub(helpers, 'getWorkspaceRoot');
getWorkspaceRootMock.returns(path.join(__filename, '../../TruffleCommandsTests/testData'));
sinon.stub(TruffleConfiguration.TruffleConfig.prototype, 'getConfiguration')
.returns(Promise.resolve({ contracts_directory: uuid.v4() } as TruffleConfiguration.IConfiguration));
getAssetsStatusStub = sinon.stub(OpenZeppelinService, 'getAssetsStatus');
downloadAssetsAsyncStub = sinon.stub(OpenZeppelinService, 'downloadAssetsAsync');
updateProjectJsonAsyncStub = sinon.stub(OpenZeppelinService, 'updateProjectJsonAsync')
.resolves();
generateMigrationsStub = sinon.stub(OpenZeppelinMigrationsService, 'generateMigrations')
.resolves();
getCategoriesStub = sinon.spy(mockOpenZeppelinManifest, 'getCategories');
collectAssetsWithDependenciesStub = sinon.spy(mockOpenZeppelinManifest, 'collectAssetsWithDependencies');
getCategoryApiDocumentationUrlStub = sinon.stub(mockOpenZeppelinManifest, 'getCategoryApiDocumentationUrl');
sinon.stub(openZeppelinHelper, 'tryGetCurrentOpenZeppelinVersionAsync').resolves('version');
sinon.stub(openZeppelinHelper, 'createManifestAsync').resolves(mockOpenZeppelinManifest);
getAssetsStatusStub.returns({ existing: [], missing: testAssets });
showQuickPickStub = sinon.stub(window, 'showQuickPick');
showInformationMessageStub = sinon.stub(window, 'showInformationMessage');
showErrorMessageStub = sinon.stub(window, 'showErrorMessage');
withProgressStub = sinon.stub(window, 'withProgress');
withProgressStub.callsFake(async (...args: any[]) => {
return args[1]();
});
showQuickPickStub.callsFake(async (...args: any[]) => {
return args[0].find((arg: any) => arg.label === selectedCategory.name);
});
const testDownloadingResult: IDownloadingResult[] = [];
testAssets.forEach((asset) => {
testDownloadingResult.push({
asset,
state: PromiseState.fulfilled,
});
});
downloadAssetsAsyncStub.resolves(testDownloadingResult);
openStub = sinon.stub().resolves();
openZeppelinCommandsRewire = rewire('../../src/commands/OpenZeppelinCommands');
openZeppelinCommandsRewire.__set__('open', openStub);
});
afterEach(() => {
sinon.restore();
});
it('addCategory should complete basic pipeline', async () => {
// Arrange
getCategoryApiDocumentationUrlStub.returns('testUrl');
const wereDownloadedMessage = Constants.openZeppelin.wereDownloaded(selectedCategory.assets.length);
showInformationMessageStub.onCall(0).returns(Constants.openZeppelin.moreDetailsButtonTitle);
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(getCategoriesStub.called, true, 'getCategories should be called.');
assert.strictEqual(showQuickPickStub.calledOnce, true, 'showQuickPick should called once.');
assert.deepStrictEqual(
showQuickPickStub.args[0][1],
{
ignoreFocusOut: true,
placeHolder: Constants.openZeppelin.selectCategoryForDownloading,
},
'selectCategory should ask for category.',
);
assert.strictEqual(collectAssetsWithDependenciesStub.calledOnce,
true, 'collectAssetsWithDependencies should called once.');
assert.deepStrictEqual(
collectAssetsWithDependenciesStub.args[0][0],
selectedCategory.assets,
'collectAssetsWithDependencies should called once with asserts from selected category.',
);
assert.strictEqual(downloadAssetsAsyncStub.calledOnce, true, 'downloadAssetsAsync should be called');
assert.strictEqual(showInformationMessageStub.calledTwice,
true, 'showInformationMessage should be called twice');
assert.strictEqual(
showInformationMessageStub.args[0][0],
wereDownloadedMessage,
'should be displayed message with number of downloaded items',
);
assert.strictEqual(
showInformationMessageStub.args[1][0],
Constants.openZeppelin.exploreDownloadedContractsInfo,
'should be displayed message with information about downloaded category',
);
assert.strictEqual(updateProjectJsonAsyncStub.calledOnce, true, 'updateProjectJsonAsync should be called once');
assert.strictEqual(
getCategoryApiDocumentationUrlStub.calledOnce,
true,
'getCategoryApiDocumentationUrl should be called',
);
assert.strictEqual(generateMigrationsStub.called, true, 'generateMigrations should be called');
});
it('addCategory should downloads selected category', async () => {
// Arrange
const showQuickPick = sinon.stub(helpers, 'showQuickPick');
showQuickPick.callsFake(async (...args: any[]) => {
return args[0];
});
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(downloadAssetsAsyncStub.args[0][1], testAssets, 'downloadAssetsAsync should called for selected asset');
});
it('addCategory throws cancellation event when user don`t select category', async () => {
// Arrange
const notExistingCategoryName = 'notExistingCategoryName';
showQuickPickStub.callsFake(async (...args: any[]) => {
return args[0].find((arg: any) => arg.label === notExistingCategoryName);
});
// Act and Assert
await assert.rejects(
async () => await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory(),
CancellationEvent,
'addCategory should throw cancellation event.');
assert.strictEqual(getCategoriesStub.calledOnce, true, 'getCategories should called once.');
assert.strictEqual(showQuickPickStub.calledOnce, true, 'showQuickPick should called once.');
assert.strictEqual(
collectAssetsWithDependenciesStub.notCalled,
true,
'collectAssetsWithDependencies should not called.');
assert.strictEqual(withProgressStub.notCalled, true, 'withProgress should not called.');
});
it('addCategory should ask for overwrite existing files and overwrite on positive answer', async () => {
// Arrange
const existingAsset = testAssets.slice(3, 4);
const missingAsset = testAssets.slice(0, 3);
getAssetsStatusStub
.returns({
existing: existingAsset,
missing: missingAsset,
});
showInformationMessageStub.onCall(0).returns(Constants.openZeppelin.replaceButtonTitle);
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(showInformationMessageStub.calledTwice, true, 'showInformationMessage should called twice');
assert.strictEqual(
showInformationMessageStub.args[0][0],
Constants.openZeppelin.alreadyExisted(existingAsset),
'alreadyExisted message should displayed once.',
);
assert.strictEqual(downloadAssetsAsyncStub.args[0][2], true, 'downloading should be called with overwrite flag');
assert.strictEqual(downloadAssetsAsyncStub.args[0][1].length, testAssets.length, 'downloading full asset of items');
});
it('addCategory should ask for overwrite existed files and skip files on negative answer', async () => {
// Arrange
const existingAsset = testAssets.slice(3, 4);
const missingAsset = testAssets.slice(0, 3);
getAssetsStatusStub
.returns({
existing: existingAsset,
missing: missingAsset,
});
showInformationMessageStub.onCall(0).returns(Constants.openZeppelin.skipButtonTitle);
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(showInformationMessageStub.calledTwice, true, 'showInformationMessage should called twice');
assert.strictEqual(
showInformationMessageStub.args[0][0],
Constants.openZeppelin.alreadyExisted(existingAsset),
'showQuickPick should called once.',
);
assert.strictEqual(downloadAssetsAsyncStub.args[0][2], false,
'downloading should be called without overwrite flag');
assert.strictEqual(downloadAssetsAsyncStub.args[0][1].length, missingAsset.length,
'downloading only missing assets');
});
it('addCategory should show error message if some files failed on downloading and allow to retry', async () => {
// Arrange
const rejectedAssets = [
{ asset: {} as IOZAsset, state: PromiseState.rejected },
{ asset: {} as IOZAsset, state: PromiseState.rejected },
];
downloadAssetsAsyncStub.resolves([
{ asset: {} as IOZAsset, state: PromiseState.fulfilled },
...rejectedAssets,
]);
showErrorMessageStub
.onCall(0).returns(Constants.openZeppelin.retryButtonTitle)
.onCall(1).returns(Constants.openZeppelin.cancelButtonTitle);
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(
showErrorMessageStub.args[0][0],
Constants.openZeppelin.wereNotDownloaded(rejectedAssets.length),
'showErrorMessageStub should called with exact message',
);
assert.strictEqual(downloadAssetsAsyncStub.calledTwice, true, 'downloadFiles should be retried');
});
it('addCategory should open documentation for chosen category after downloading', async () => {
// Arrange
const testDocumentationUrl = 'testUrl';
getCategoryApiDocumentationUrlStub.returns(testDocumentationUrl);
generateMigrationsStub.resolves();
showInformationMessageStub.onCall(1).returns(Constants.openZeppelin.moreDetailsButtonTitle);
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(getCategoryApiDocumentationUrlStub.calledWithMatch(selectedCategory), true,
'getCategoryApiDocumentationUrl should be called with chosen category');
assert.strictEqual(openStub.calledWith(testDocumentationUrl), true,
`open should be called with ${testDocumentationUrl}`);
assert.strictEqual(openStub.calledAfter(downloadAssetsAsyncStub), true,
'open should be called after downloadFiles');
});
it('addCategory should not ask and open category documentation if it doesn\'t exist', async () => {
// Arrange
getCategoryApiDocumentationUrlStub.returns(undefined);
generateMigrationsStub.resolves();
// Act
await openZeppelinCommandsRewire.OpenZeppelinCommands.addCategory();
// Assert
assert.strictEqual(
showInformationMessageStub.calledWith(Constants.openZeppelin.exploreDownloadedContractsInfo),
false,
'showInformationMessageStub should not be called with explore contracts info message');
assert.strictEqual(openStub.called, false, 'open should not be called');
});
});
function getTestCategories(): IOZContractCategory[] {
const testCategories: IOZContractCategory[] = [{
assets: [uuid.v4()],
id: uuid.v4(),
name: uuid.v4(),
},
{
assets: [uuid.v4(), uuid.v4()],
id: uuid.v4(),
name: uuid.v4(),
},
{
assets: [uuid.v4(), uuid.v4(), uuid.v4(), uuid.v4()],
id: uuid.v4(),
name: uuid.v4(),
}];
return testCategories;
}
function getTestAssetsWithDependencies(assets: string[]): IOZAsset[] {
const assetsWithDependencies: IOZAsset[] = [];
assets.forEach((asset) => {
assetsWithDependencies.push({
dependencies: [uuid.v4()],
hash: uuid.v4(),
id: asset,
name: uuid.v4(),
type: OZAssetType.contract,
});
});
return assetsWithDependencies;
}

Просмотреть файл

@ -28,10 +28,18 @@
"./src/debugger/types"
]
},
"include": ["src/**/*.js", "src/**/*.ts", "src/**/*.json", "test/**/*.js", "test/**/*.ts", "test/**/*.json"],
"include": [
"src/**/*.js",
"src/**/*.ts",
"src/**/*.json",
"test/**/*.js",
"test/**/*.ts",
"test/**/*.json"
],
"exclude": [
"node_modules",
".vscode-test",
"drizzleUI"
"drizzleUI",
"ui-test"
]
}
}

37
tsconfig.json.orig Normal file
Просмотреть файл

@ -0,0 +1,37 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6",
"dom"
],
"sourceMap": true,
"resolveJsonModule": true,
"rootDir": ".",
"strict": true,
/* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
"noUnusedLocals": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noUnusedParameters": true,
"allowJs": true,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@machinomy",
"./src/debugger/types"
]
},
"include": ["src/**/*.js", "src/**/*.ts", "src/**/*.json", "test/**/*.js", "test/**/*.ts", "test/**/*.json"],
"exclude": [
"node_modules",
".vscode-test",
"drizzleUI"
]
}

6
ui-test/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
out
node_modules
.vscode-test/
*.vsix
test-resources/
package-lock.json

1
ui-test/.vscodeignore Normal file
Просмотреть файл

@ -0,0 +1 @@
test-resources/**

34
ui-test/package.json Normal file
Просмотреть файл

@ -0,0 +1,34 @@
{
"name": "ui-test",
"version": "1.0.0",
"description": "End-to-end tests for azure blockchain extension",
"main": "index.js",
"publisher": "AzBlockchain",
"engines": {
"vscode": "^1.43.0"
},
"scripts": {
"clean": "npx rimraf -- ./out/*",
"compile": "npm run clean && tsc -p ./",
"lint": "tslint -p ./",
"watch": "tsc -watch -p ./",
"download": "download --out test-resources --filename azure-blockchain.vsix https://marketplace.visualstudio.com/_apis/public/gallery/publishers/AzBlockchain/vsextensions/azure-blockchain/1.2.0/vspackage",
"prepare-ui-test": "npm run compile && npm run download && extest get-vscode && extest get-chromedriver && extest install-vsix -f test-resources/azure-blockchain.vsix",
"ui-test": "npm run prepare-ui-test && extest run-tests out/*.test.js"
},
"keywords": [],
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.2.11",
"@types/fs-extra": "^7.0.0",
"@types/mocha": "^7.0.2",
"chai": "^4.2.0",
"download-cli": "^1.1.1",
"fs-extra": "^7.0.0",
"mocha": "^7.1.1",
"tslint": "^5.18.0",
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.3.1",
"vscode-extension-tester": "^2.6.0"
}
}

6
ui-test/src/config.ts Normal file
Просмотреть файл

@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export const testProjectDirectory = '';
export const commonTestTimeout = 120000;
export const setupTimeout = 300000;

Просмотреть файл

@ -0,0 +1,269 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { expect } from 'chai';
import * as clipboard from 'clipboardy';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import {
ActivityBar,
BottomBarPanel,
CustomTreeSection,
DefaultTreeSection,
DialogHandler,
InputBox,
OutputView,
SideBarView,
TreeItem,
VSBrowser,
WebDriver,
Workbench,
} from 'vscode-extension-tester';
import { commonTestTimeout, setupTimeout, testProjectDirectory } from './config';
const tempProjectPrefix = 'ui-test-';
describe('Truffle tests', function() {
this.timeout(setupTimeout);
let driver: WebDriver;
let workbench: Workbench;
let testDir: string;
before(async () => {
testDir = testProjectDirectory || createTempFolder();
driver = VSBrowser.instance.driver;
await driver.manage().timeouts().implicitlyWait(30000);
await driver.manage().timeouts().setScriptTimeout(30000);
await driver.manage().timeouts().pageLoadTimeout(30000);
workbench = new Workbench();
await createNewSolidityProject(driver, workbench, testDir);
});
after(async () => {
await openWorkspace(workbench, os.tmpdir());
await reload(workbench);
await removeTempFolder(testDir);
});
describe('Truffle tests', () => {
it('Create new network', async () => {
const testNetworkName = 'testnetwork';
await workbench.executeCommand('Azure Blockchain: Create a new network');
const serviceTypeInput = await driver.wait(() => new InputBox(), 5000);
await serviceTypeInput.setText('Local Service');
await serviceTypeInput.confirm();
const networkNameInput = await driver.wait(() => new InputBox(), 5000);
await networkNameInput.setText(testNetworkName);
await networkNameInput.confirm();
const networkPortInput = await driver.wait(() => new InputBox(), 5000);
await networkPortInput.setText('8535');
await networkPortInput.confirm();
const azureBlockchainSection = <CustomTreeSection> await new SideBarView()
.getContent()
.getSection('Azure Blockchain');
await azureBlockchainSection.expand();
const localServiceSection = await azureBlockchainSection.findItem('Local Service');
expect(localServiceSection).instanceOf(TreeItem);
const localServices = await localServiceSection?.getChildren();
const localServicesNames = localServices?.map((service) => service.getLabel());
expect(localServicesNames?.includes(testNetworkName)).equals(true, 'New network name should appear in the tree');
}).timeout(commonTestTimeout);
it('Build contracts', async () => {
const output = await driver.wait(() => new BottomBarPanel().openOutputView(), 5000);
await output.selectChannel('Azure Blockchain');
await output.clearText();
await workbench.executeCommand('Azure Blockchain: Build Contracts');
await scanOutput(driver, output, 'Compiled successfully using', 60000, 2000, 5000);
const directoryFiles = fs.readdirSync(path.join(testDir, 'build', 'contracts'));
expect(directoryFiles.length).not.equals(0);
}).timeout(commonTestTimeout);
it('Deploy contracts', async () => {
const output = await driver.wait(() => new BottomBarPanel().openOutputView(), 5000);
await output.selectChannel('Azure Blockchain');
await output.clearText();
await workbench.executeCommand('Azure Blockchain: Deploy Contracts');
const destinationInput = await driver.wait(() => new InputBox(), 60000);
await destinationInput.setText('loc_testnetwork_testnetwork');
await destinationInput.confirm();
await scanOutput(driver, output, 'Deploy succeeded', 60000, 2000, 5000);
}).timeout(commonTestTimeout);
describe('Abi and bytecode tests', () => {
let buildContract: string;
let contract: TreeItem | undefined;
before(async () => {
const workspaceSection = <DefaultTreeSection> await new SideBarView().getContent().getSection('Untitled (Workspace)');
const projectName = testDir.match(new RegExp(`(${tempProjectPrefix}).*$`))![0];
const project = await workspaceSection.findItem(projectName);
await project?.click();
const buildFolder = await project?.findChildItem('build');
await buildFolder?.click();
contract = await workspaceSection.findItem('HelloBlockchain.json');
await contract?.select();
buildContract = fs.readFileSync(path.join(testDir, 'build', 'contracts', 'HelloBlockchain.json')).toString();
});
it('Copy Contract ABI command should be executed', async () => {
const menu = await contract?.openContextMenu();
const menuItem = await menu?.getItem('Copy Contract ABI');
await menuItem?.select();
await driver.sleep(1000);
const abiBuffer = clipboard.readSync();
expect(JSON.stringify(JSON.parse(buildContract)['abi'])).equals(abiBuffer);
}).timeout(commonTestTimeout);
it('Copy Transaction Bytecode command should be executed', async () => {
const menu = await contract?.openContextMenu();
const menuItem = await menu?.getItem('Copy Transaction Bytecode');
await menuItem?.select();
const destinationInput = await driver.wait(() => new InputBox(), 60000);
await destinationInput.setText('loc_testnetwork_testnetwork');
await destinationInput.confirm();
const notifications = await new Workbench().getNotifications();
const notification = notifications[0];
const message = await notification.getMessage();
const deployedBytecode = clipboard.readSync();
expect(message.includes('Transaction Bytecode was copied to clipboard')).equals(true, 'Notification should appear');
expect(deployedBytecode).not.equals(undefined, 'Bytecode should be copied to clipboard');
}).timeout(commonTestTimeout);
it('Copy Constructor Bytecode command should be executed', async () => {
const menu = await contract?.openContextMenu();
const menuItem = await menu?.getItem('Copy Constructor Bytecode');
await menuItem?.select();
await driver.sleep(1000);
const bytecode = clipboard.readSync();
expect(JSON.parse(buildContract)['bytecode']).equals(bytecode);
}).timeout(commonTestTimeout);
});
});
});
async function scanOutput(
driver: WebDriver,
output: OutputView,
successMessage: string,
maxWait: number,
timeout: number,
initialWait: number) {
return driver.wait(async () => {
return new Promise((resolve, reject) => {
setTimeout(async function waiter() {
try {
const text = await output.getText();
if (text.includes(successMessage)) {
return resolve('Done');
}
} catch (error) {
reject(error);
}
setTimeout(waiter, timeout);
}, initialWait);
});
}, maxWait);
}
function createTempFolder() {
return fs.mkdtempSync(path.join(os.tmpdir(), tempProjectPrefix));
}
async function removeTempFolder(directory: string) {
await fs.remove(directory);
}
async function openWorkspace(workbench: Workbench, directory: string): Promise<void> {
await workbench.executeCommand('Extest: Open Folder');
const input = await InputBox.create();
await input.setText(directory);
await input.confirm();
}
async function reload(workbench: Workbench) {
await workbench.executeCommand('Developer: Reload Window');
}
async function createNewSolidityProject(
driver: WebDriver,
workbench: Workbench,
testDirectory: string): Promise<void> {
// we should close all previous notification because they can affect command execution
// notification center work very slow
const notificationCenter = await driver.wait(() => workbench.openNotificationsCenter(), 30000);
await notificationCenter.clearAllNotifications();
const activityBar = new ActivityBar();
const explorer = await activityBar.getViewControl('Explorer');
await explorer.openView();
await workbench.executeCommand('Azure Blockchain: New Solidity Project');
const input = await driver.wait(() => new InputBox(), 60000);
await input.setText('Create basic project');
await input.confirm();
const dialog = await driver.wait(() => DialogHandler.getOpenDialog(), 5000);
await dialog.selectPath(testDirectory);
await dialog.confirm();
const output = await driver.wait(() => new BottomBarPanel().openOutputView(), 5000);
await output.selectChannel('Azure Blockchain');
await driver.wait(async () => {
return new Promise((resolve, reject) => {
setTimeout(async function waitForUnboxing() {
try {
const text = await output.getText();
if (text.includes('Error')) {
return reject(new Error('Error on unboxing'));
}
if (fs.existsSync(path.join(testDirectory, 'package-lock.json')) ||
text.includes('Initialized empty Git repository')) {
return resolve('Done');
}
} catch (error) {
reject(error);
}
setTimeout(waitForUnboxing, 3000);
}, 20000);
});
}, 120000);
// wait for change directory during project creation
await driver.sleep(10000);
}

21
ui-test/tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES6",
"outDir": "out",
"sourceMap": true,
"strict": true,
"rootDir": "src",
"allowJs": true
},
"include": [
"src/**/*.js",
"src/**/*.ts",
"src/**/*.json"
],
"exclude": [
"node_modules",
"vscode-test",
"test-resources"
]
}

51
ui-test/tslint.json Normal file
Просмотреть файл

@ -0,0 +1,51 @@
{
"extends": ["tslint-microsoft-contrib/recommended"],
"rules": {
"no-string-throw": true,
"no-unused-expression": true,
"no-duplicate-variable": true,
"no-namespace": false,
"no-relative-imports": false,
"no-function-expression": false,
"non-literal-fs-path": false,
"mocha-no-side-effect-code": false,
"curly": true,
"class-name": true,
"semicolon": [
true,
"always"
],
"triple-equals": true,
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-module",
"check-separator",
"check-rest-spread",
"check-type",
"check-typecast",
"check-type-operator"
],
"quotemark": [
true,
"single",
"avoid-escape",
"avoid-template"
]
},
"linterOptions": {
"exclude": [
"node_modules/**",
"src/**/*.d.ts"
]
},
"defaultSeverity": "warning"
}