From 7a0439d3590adfeb3185e8e36867388e6a558dd7 Mon Sep 17 00:00:00 2001 From: Cale Teeter Date: Fri, 31 Jan 2020 12:22:42 -0500 Subject: [PATCH] Updates for release v1.0.0 --- CHANGELOG.md | 16 + package.json | 24 +- ...ckchainDataManager-service_and_project.svg | 11 + .../dark/BlockchainDataManagerApplication.svg | 27 + .../dark/BlockchainDataManagerGroupInput.svg | 10 + .../dark/BlockchainDataManagerGroupOutput.svg | 10 + .../dark/BlockchainDataManagerOutput.svg | 12 + ...ckchainDataManager-service_and_project.svg | 11 + .../BlockchainDataManagerApplication.svg | 27 + .../light/BlockchainDataManagerGroupInput.svg | 10 + .../BlockchainDataManagerGroupOutput.svg | 10 + .../light/BlockchainDataManagerOutput.svg | 12 + .../AzureBlockchainServiceClient.ts | 60 +- .../BlockchainDataManagerApplicationDto.ts | 20 + .../AzureDto/BlockchainDataManagerDto.ts | 20 + .../AzureDto/BlockchainDataManagerInputDto.ts | 14 + .../BlockchainDataManagerOutputDto.ts | 17 + .../BlockchainDataManagerResource.ts | 64 + src/ARMBlockchain/index.ts | 8 + src/Constants.ts | 279 ++- src/Generators/LogicAppGenerator.ts | 4 +- ...ainDataManagerInputAndOutputItemCreator.ts | 32 + ...kchainDataManagerNetworkNodeItemCreator.ts | 40 + ...BlockchainDataManagerProjectItemCreator.ts | 34 + ...BlockchainDataManagerServiceItemCreator.ts | 11 + src/Models/ItemCreators/index.ts | 4 + src/Models/ItemType.ts | 8 + .../BlockchainDataManagerInstanceItem.ts | 21 + src/Models/QuickPickItems/index.ts | 1 + .../BlockchainDataManagerInputAndOutput.ts | 20 + .../BlockchainDataManagerNetworkNode.ts | 61 + .../BlockchainDataManagerProject.ts | 36 + .../BlockchainDataManagerService.ts | 16 + src/Models/TreeItems/Group.ts | 16 + src/Models/TreeItems/Member.ts | 6 +- src/Models/TreeItems/NetworkNode.ts | 5 +- src/Models/TreeItems/Project.ts | 6 +- src/Models/TreeItems/Service.ts | 1 + src/Models/TreeItems/index.ts | 5 + src/Models/index.ts | 15 + src/ViewItems/GroupView.ts | 11 + src/ViewItems/MemberView.ts | 11 - .../ViewCreators/GroupViewCreator.ts | 12 + .../ViewCreators/MemberViewCreator.ts | 12 - src/ViewItems/ViewCreators/index.ts | 2 +- src/ViewItems/index.ts | 13 +- src/commands/OpenZeppelinCommands.ts | 9 +- src/commands/ServiceCommands.ts | 34 +- src/commands/TruffleCommands.ts | 20 +- src/extension.ts | 7 +- src/helpers/bigIntMath.ts | 8 + src/helpers/checkTruffleConfigTemplate.js | 36 +- src/helpers/openZeppelinHelper.ts | 100 +- src/helpers/truffleConfig.ts | 18 +- src/helpers/userInteraction.ts | 9 +- .../BlockchainDataManagerResourceExplorer.ts | 211 +++ .../ConsortiumResourceExplorer.ts | 1 - src/resourceExplorers/index.ts | 1 + src/services/contract/ContractService.ts | 3 +- src/services/index.ts | 1 + .../OpenZeppelinMigrationsService.ts | 5 +- .../OpenZeppelinProjectJsonService.ts | 21 +- .../openZeppelin/OpenZeppelinService.ts | 73 +- src/services/openZeppelin/manifest-2.3.0.json | 1675 +++++++++++------ src/services/openZeppelin/manifest-2.4.0.json | 750 +++++++- src/services/openZeppelin/models.ts | 7 + src/services/tree/TreeManager.ts | 8 +- .../solidityTypeValidation/index.ts | 85 + .../isConfirmationValue.ts | 2 +- test/AzureBlockchainServiceClient.int.test.ts | 198 ++ test/AzureBlockchainServiceClient.test.ts | 954 ++-------- test/TreeManager.test.ts | 2 +- .../WriteToBuffer.test.ts.orig | 72 - .../deployContracts.test.ts | 54 +- .../deployContracts.test.ts.orig | 671 ------- test/commands.test.ts | 66 + .../OpenZeppelinCommands.test.ts.orig | 335 ---- .../connectService.integration.test.ts | 84 +- .../ServiceCommands/connectService.test.ts | 62 +- test/debugAdapter/helper.test.ts.orig | 67 - test/openZeppelinHelper.test.ts | 2 +- test/required.test.ts.orig | 744 -------- test/testData/truffleConfigTestdata.ts.orig | 106 -- test/truffleConfig.test.ts.orig | 286 --- test/userInteraction.test.ts.orig | 214 --- tslint.json | 3 +- 86 files changed, 4017 insertions(+), 4052 deletions(-) create mode 100644 resources/dark/BlockchainDataManager-service_and_project.svg create mode 100644 resources/dark/BlockchainDataManagerApplication.svg create mode 100644 resources/dark/BlockchainDataManagerGroupInput.svg create mode 100644 resources/dark/BlockchainDataManagerGroupOutput.svg create mode 100644 resources/dark/BlockchainDataManagerOutput.svg create mode 100644 resources/light/BlockchainDataManager-service_and_project.svg create mode 100644 resources/light/BlockchainDataManagerApplication.svg create mode 100644 resources/light/BlockchainDataManagerGroupInput.svg create mode 100644 resources/light/BlockchainDataManagerGroupOutput.svg create mode 100644 resources/light/BlockchainDataManagerOutput.svg create mode 100644 src/ARMBlockchain/AzureDto/BlockchainDataManagerApplicationDto.ts create mode 100644 src/ARMBlockchain/AzureDto/BlockchainDataManagerDto.ts create mode 100644 src/ARMBlockchain/AzureDto/BlockchainDataManagerInputDto.ts create mode 100644 src/ARMBlockchain/AzureDto/BlockchainDataManagerOutputDto.ts create mode 100644 src/ARMBlockchain/Operations/BlockchainDataManagerResource.ts create mode 100644 src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerInputAndOutputItemCreator.ts create mode 100644 src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerNetworkNodeItemCreator.ts create mode 100644 src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerProjectItemCreator.ts create mode 100644 src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerServiceItemCreator.ts create mode 100644 src/Models/QuickPickItems/BlockchainDataManagerInstanceItem.ts create mode 100644 src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerInputAndOutput.ts create mode 100644 src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerNetworkNode.ts create mode 100644 src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerProject.ts create mode 100644 src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerService.ts create mode 100644 src/Models/TreeItems/Group.ts create mode 100644 src/ViewItems/GroupView.ts delete mode 100644 src/ViewItems/MemberView.ts create mode 100644 src/ViewItems/ViewCreators/GroupViewCreator.ts delete mode 100644 src/ViewItems/ViewCreators/MemberViewCreator.ts create mode 100644 src/helpers/bigIntMath.ts create mode 100644 src/resourceExplorers/BlockchainDataManagerResourceExplorer.ts create mode 100644 src/validators/solidityTypeValidation/index.ts create mode 100644 test/AzureBlockchainServiceClient.int.test.ts delete mode 100644 test/TruffleCommandsTests/WriteToBuffer.test.ts.orig delete mode 100644 test/TruffleCommandsTests/deployContracts.test.ts.orig delete mode 100644 test/commands/OpenZeppelinCommands.test.ts.orig delete mode 100644 test/debugAdapter/helper.test.ts.orig delete mode 100644 test/required.test.ts.orig delete mode 100644 test/testData/truffleConfigTestdata.ts.orig delete mode 100644 test/truffleConfig.test.ts.orig delete mode 100644 test/userInteraction.test.ts.orig diff --git a/CHANGELOG.md b/CHANGELOG.md index f24e04f..9c7ef5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to the "azure blockchain" extension will be documented in this file. +## 0.1.14 + +### 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 diff --git a/package.json b/package.json index aa283a1..cb68645 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "displayName": "Azure Blockchain Development Kit for Ethereum", "description": "Develop, deploy debug and manage your Azure Blockchain Service solution", "publisher": "AzBlockchain", - "preview": true, + "preview": false, "icon": "images/blockchain-service-logo.png", - "version": "0.1.13", + "version": "1.0.0", "repository": { "type": "git", "url": "https://github.com/Microsoft/vscode-azure-blockchain-ethereum" @@ -57,6 +57,7 @@ "onCommand:azureBlockchainService.signInToInfuraAccount", "onCommand:azureBlockchainService.signOutOfInfuraAccount", "onCommand:azureBlockchainService.generateToken", + "onCommand:azureBlockchainService.openAtAzurePortal", "onDebug" ], "contributes": { @@ -204,6 +205,11 @@ "command": "azureBlockchainService.generateToken", "title": "Generate token", "category": "Azure Blockchain" + }, + { + "command": "azureBlockchainService.openAtAzurePortal", + "title": "Open at Azure Portal", + "category": "Azure Blockchain" } ], "breakpoints": [ @@ -275,6 +281,10 @@ { "when": "false", "command": "openZeppelin.addCategory" + }, + { + "when": "false", + "command": "azureBlockchainService.openAtAzurePortal" } ], "view/title": [ @@ -326,6 +336,16 @@ "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" } ], "explorer/context": [ diff --git a/resources/dark/BlockchainDataManager-service_and_project.svg b/resources/dark/BlockchainDataManager-service_and_project.svg new file mode 100644 index 0000000..23ed055 --- /dev/null +++ b/resources/dark/BlockchainDataManager-service_and_project.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/resources/dark/BlockchainDataManagerApplication.svg b/resources/dark/BlockchainDataManagerApplication.svg new file mode 100644 index 0000000..e687b1e --- /dev/null +++ b/resources/dark/BlockchainDataManagerApplication.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dark/BlockchainDataManagerGroupInput.svg b/resources/dark/BlockchainDataManagerGroupInput.svg new file mode 100644 index 0000000..4d25f68 --- /dev/null +++ b/resources/dark/BlockchainDataManagerGroupInput.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/dark/BlockchainDataManagerGroupOutput.svg b/resources/dark/BlockchainDataManagerGroupOutput.svg new file mode 100644 index 0000000..a116e39 --- /dev/null +++ b/resources/dark/BlockchainDataManagerGroupOutput.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/dark/BlockchainDataManagerOutput.svg b/resources/dark/BlockchainDataManagerOutput.svg new file mode 100644 index 0000000..64944a5 --- /dev/null +++ b/resources/dark/BlockchainDataManagerOutput.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/resources/light/BlockchainDataManager-service_and_project.svg b/resources/light/BlockchainDataManager-service_and_project.svg new file mode 100644 index 0000000..23ed055 --- /dev/null +++ b/resources/light/BlockchainDataManager-service_and_project.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/resources/light/BlockchainDataManagerApplication.svg b/resources/light/BlockchainDataManagerApplication.svg new file mode 100644 index 0000000..e687b1e --- /dev/null +++ b/resources/light/BlockchainDataManagerApplication.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/light/BlockchainDataManagerGroupInput.svg b/resources/light/BlockchainDataManagerGroupInput.svg new file mode 100644 index 0000000..54a943c --- /dev/null +++ b/resources/light/BlockchainDataManagerGroupInput.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/light/BlockchainDataManagerGroupOutput.svg b/resources/light/BlockchainDataManagerGroupOutput.svg new file mode 100644 index 0000000..d7fcfc8 --- /dev/null +++ b/resources/light/BlockchainDataManagerGroupOutput.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/resources/light/BlockchainDataManagerOutput.svg b/resources/light/BlockchainDataManagerOutput.svg new file mode 100644 index 0000000..64944a5 --- /dev/null +++ b/resources/light/BlockchainDataManagerOutput.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/ARMBlockchain/AzureBlockchainServiceClient.ts b/src/ARMBlockchain/AzureBlockchainServiceClient.ts index 3d33252..634b494 100644 --- a/src/ARMBlockchain/AzureBlockchainServiceClient.ts +++ b/src/ARMBlockchain/AzureBlockchainServiceClient.ts @@ -9,16 +9,20 @@ import { Constants } from '../Constants'; import { vscodeEnvironment } from '../helpers'; import { Output } from '../Output'; import { Telemetry } from '../TelemetryClient'; +import { BlockchainDataManagerResource } from './Operations/BlockchainDataManagerResource'; import { ConsortiumResource } from './Operations/ConsortiumResource'; import { MemberResource } from './Operations/MemberResource'; import { SkuResource } from './Operations/SkuResources'; import { TransactionNodeResource } from './Operations/TransactionNodeResource'; +const { preview20180601, preview20190601 } = Constants.azureApiVersions; + export class AzureBlockchainServiceClient extends AzureServiceClient { public memberResource: MemberResource; public transactionNodeResource: TransactionNodeResource; public consortiumResource: ConsortiumResource; public skuResource: SkuResource; + public bdmResource: BlockchainDataManagerResource; constructor( credentials: ServiceClientCredentials | UserTokenCredentials, @@ -26,7 +30,6 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { public readonly resourceGroup: string, public readonly location: string, public readonly baseUri: string, - public readonly apiVersion: string, public readonly options: AzureServiceClientOptions, ) { super(credentials, options); @@ -49,10 +52,11 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { this.transactionNodeResource = new TransactionNodeResource(this); this.consortiumResource = new ConsortiumResource(this); this.skuResource = new SkuResource(this); + this.bdmResource = new BlockchainDataManagerResource(this); } public async createConsortium(memberName: string, body: string): Promise { - const url = this.getUrl(memberName, true, true); + const url = this.getUrl(memberName, preview20180601, true, true); const urlDetailsOfConsortium = url.slice(url.indexOf('subscriptions'), url.lastIndexOf(memberName)) + memberName; const httpRequest = this.getHttpRequest(url, 'PUT', body); @@ -78,8 +82,40 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { }); } + public getBlockchainDataManagers(callback: (error: Error | null, result?: any) => void): Promise { + const url = this.getUrl('watchers', preview20190601, true, false); + + const httpRequest = this.getHttpRequest(url, 'GET'); + return this.sendRequestToAzure(httpRequest, callback); + } + + public getBlockchainDataManagerApplications(bdmName: string, callback: (error: Error | null, result?: any) => void) + : Promise { + const url = + this.getUrl(`watchers/${bdmName}/artifacts`, preview20190601, true, false); + + const httpRequest = this.getHttpRequest(url, 'GET'); + return this.sendRequestToAzure(httpRequest, callback); + } + + public getBlockchainDataManagerInputs(bdmName: string, callback: (error: Error | null, result?: any) => void) + : Promise { + const url = this.getUrl(`watchers/${bdmName}/inputs`, preview20190601, true, false); + + const httpRequest = this.getHttpRequest(url, 'GET'); + return this.sendRequestToAzure(httpRequest, callback); + } + + public getBlockchainDataManagerOutputs(bdmName: string, callback: (error: Error | null, result?: any) => void) + : Promise { + const url = this.getUrl(`watchers/${bdmName}/outputs`, preview20190601, true, false); + + const httpRequest = this.getHttpRequest(url, 'GET'); + return this.sendRequestToAzure(httpRequest, callback); + } + public getMembers(memberName: string, callback: (error: Error | null, result?: any) => void): Promise { - const url = this.getUrl(`${memberName}/ConsortiumMembers`, true, true); + const url = this.getUrl(`${memberName}/ConsortiumMembers`, preview20180601, true, true); const httpRequest = this.getHttpRequest(url, 'GET'); @@ -87,7 +123,7 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { } public getConsortia(callback: (error: Error | null, result?: any) => void): Promise { - const url = this.getUrl('', true, true); + const url = this.getUrl('', preview20180601, true, true); const httpRequest = this.getHttpRequest(url, 'GET'); @@ -95,7 +131,7 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { } public getTransactionNodes(memberName: string, callback: (error: Error | null, result?: any) => void): Promise { - const url = this.getUrl(`${memberName}/transactionNodes`, true, true); + const url = this.getUrl(`${memberName}/transactionNodes`, preview20180601, true, true); const httpRequest = this.getHttpRequest(url, 'GET'); @@ -111,7 +147,7 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { ? `${memberName}/listApikeys` : `${memberName}/transactionNodes/${nodeName}/listApikeys`; - const url = this.getUrl(mainPartOfUrl, true, true); + const url = this.getUrl(mainPartOfUrl, preview20180601, true, true); const httpRequest = this.getHttpRequest(url, 'POST'); @@ -119,7 +155,7 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { } public getSkus(callback: (error: Error | null, result?: any) => void): Promise { - const url = this.getUrl('skus'); + const url = this.getUrl('skus', preview20180601); const httpRequest = this.getHttpRequest(url, 'GET'); @@ -180,7 +216,7 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { nameAvailable: boolean, reason: string, }> { - const url = this.getUrl(`locations/${this.location}/checkNameAvailability`); + const url = this.getUrl(`locations/${this.location}/checkNameAvailability`, preview20180601); const request = this.getHttpRequest( url, @@ -202,12 +238,16 @@ export class AzureBlockchainServiceClient extends AzureServiceClient { }); } - private getUrl(mainPartOfUrl: string, useResourceGroup: boolean = false, useBlockchainMembers: boolean = false) + private getUrl( + mainPartOfUrl: string, + apiVersion: string, + useResourceGroup: boolean = false, + useBlockchainMembers: boolean = false) : string { const resourceGroup = useResourceGroup ? `resourceGroups/${this.resourceGroup}/` : ''; const blockchainMember = useBlockchainMembers ? 'blockchainMembers/' : ''; return `${this.baseUri}/subscriptions/${this.subscriptionId}/${resourceGroup}` + - `providers/Microsoft.Blockchain/${blockchainMember}${mainPartOfUrl}?api-version=${this.apiVersion}`; + `providers/Microsoft.Blockchain/${blockchainMember}${mainPartOfUrl}?api-version=${apiVersion}`; } } diff --git a/src/ARMBlockchain/AzureDto/BlockchainDataManagerApplicationDto.ts b/src/ARMBlockchain/AzureDto/BlockchainDataManagerApplicationDto.ts new file mode 100644 index 0000000..c73cb19 --- /dev/null +++ b/src/ARMBlockchain/AzureDto/BlockchainDataManagerApplicationDto.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +export interface IAzureBlockchainDataManagerApplicationDto { + id: string; + name: string; + properties: { + artifactType: string; + content: { + abiFileUrl: string; + bytecodeFileUrl: string; + queryTargetTypes: string[]; + }; + state: string; + provisioningState: string; + createdTime: string; + lastUpdatedTime: string; + }; + type: string; +} diff --git a/src/ARMBlockchain/AzureDto/BlockchainDataManagerDto.ts b/src/ARMBlockchain/AzureDto/BlockchainDataManagerDto.ts new file mode 100644 index 0000000..7058b2c --- /dev/null +++ b/src/ARMBlockchain/AzureDto/BlockchainDataManagerDto.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ISkuDto } from '..'; + +export interface IAzureBlockchainDataManagerDto { + id: string; + location: string; + name: string; + properties: { + createdTime: string; + lastUpdatedTime: string; + provisioningState: string; + sku: ISkuDto; + state: string; + uniqueId: string; + }; + tags: {}; + type: string; +} diff --git a/src/ARMBlockchain/AzureDto/BlockchainDataManagerInputDto.ts b/src/ARMBlockchain/AzureDto/BlockchainDataManagerInputDto.ts new file mode 100644 index 0000000..b4eb35d --- /dev/null +++ b/src/ARMBlockchain/AzureDto/BlockchainDataManagerInputDto.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +export interface IAzureBlockchainDataManagerInputDto { + id: string; + name: string; + properties: { + inputType: string; + dataSource: { + resourceId: string; + }; + }; + type: string; +} diff --git a/src/ARMBlockchain/AzureDto/BlockchainDataManagerOutputDto.ts b/src/ARMBlockchain/AzureDto/BlockchainDataManagerOutputDto.ts new file mode 100644 index 0000000..27f87f5 --- /dev/null +++ b/src/ARMBlockchain/AzureDto/BlockchainDataManagerOutputDto.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +export interface IAzureBlockchainDataManagerOutputDto { + id: string; + name: string; + properties: { + outputType: string; + dataSource: { + resourceId: string; + }; + state: string; + createdTime: string; + lastUpdatedTime: string; + }; + type: string; +} diff --git a/src/ARMBlockchain/Operations/BlockchainDataManagerResource.ts b/src/ARMBlockchain/Operations/BlockchainDataManagerResource.ts new file mode 100644 index 0000000..577a4c4 --- /dev/null +++ b/src/ARMBlockchain/Operations/BlockchainDataManagerResource.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { + IAzureBlockchainDataManagerApplicationDto, + IAzureBlockchainDataManagerDto, + IAzureBlockchainDataManagerInputDto, + IAzureBlockchainDataManagerOutputDto, +} from '..'; +import { AzureBlockchainServiceClient } from '../AzureBlockchainServiceClient'; + +export class BlockchainDataManagerResource { + constructor(public readonly client: AzureBlockchainServiceClient) {} + public async getListBlockchainDataManager(): Promise { + return new Promise((resolve, reject) => { + return this.client.getBlockchainDataManagers((error: Error | null, result?: any) => { + if (error) { + reject(error); + } else { + resolve(Object.assign([], result.value)); + } + }); + }); + } + + public async getListBlockchainDataManagerApplication(bdmName: string) + : Promise { + return new Promise((resolve, reject) => { + return this.client.getBlockchainDataManagerApplications(bdmName, (error: Error | null, result?: any) => { + if (error) { + reject(error); + } else { + resolve(Object.assign([], result.value)); + } + }); + }); + } + + public async getListBlockchainDataManagerInput(bdmName: string) + : Promise { + return new Promise((resolve, reject) => { + return this.client.getBlockchainDataManagerInputs(bdmName, (error: Error | null, result?: any) => { + if (error) { + reject(error); + } else { + resolve(Object.assign([], result.value)); + } + }); + }); + } + + public async getListBlockchainDataManagerOutput(bdmName: string) + : Promise { + return new Promise((resolve, reject) => { + return this.client.getBlockchainDataManagerOutputs(bdmName, (error: Error | null, result?: any) => { + if (error) { + reject(error); + } else { + resolve(Object.assign([], result.value)); + } + }); + }); + } +} diff --git a/src/ARMBlockchain/index.ts b/src/ARMBlockchain/index.ts index e0d9438..037f79f 100644 --- a/src/ARMBlockchain/index.ts +++ b/src/ARMBlockchain/index.ts @@ -2,6 +2,10 @@ // Licensed under the MIT license. import { AzureBlockchainServiceClient } from './AzureBlockchainServiceClient'; +import { IAzureBlockchainDataManagerApplicationDto } from './AzureDto/BlockchainDataManagerApplicationDto'; +import { IAzureBlockchainDataManagerDto } from './AzureDto/BlockchainDataManagerDto'; +import { IAzureBlockchainDataManagerInputDto } from './AzureDto/BlockchainDataManagerInputDto'; +import { IAzureBlockchainDataManagerOutputDto } from './AzureDto/BlockchainDataManagerOutputDto'; import { IAzureConsortiumDto } from './AzureDto/ConsortiumDto'; import { IAzureConsortiumMemberDto } from './AzureDto/ConsortiumMemberDto'; import { IAzureMemberDto } from './AzureDto/MemberDto'; @@ -14,6 +18,10 @@ import { SkuResource } from './Operations/SkuResources'; export { AzureBlockchainServiceClient, + IAzureBlockchainDataManagerApplicationDto, + IAzureBlockchainDataManagerDto, + IAzureBlockchainDataManagerInputDto, + IAzureBlockchainDataManagerOutputDto, IAzureConsortiumDto, IAzureConsortiumMemberDto, IAzureMemberDto, diff --git a/src/Constants.ts b/src/Constants.ts index d0fa6d9..7cc5dc2 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -170,8 +170,8 @@ export class Constants { }; public static confirmationDialogResult = { - no: 'no', - yes: 'yes', + no: 'No', + yes: 'Yes', }; public static mnemonicConstants = { @@ -203,7 +203,116 @@ export class Constants { 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: 'project', + 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, @@ -217,6 +326,7 @@ export class Constants { 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: { @@ -224,6 +334,14 @@ export class Constants { password: /[!@$^&()+=?\/<>|[\]{}_:.\\~]/g, resourceGroupName: /[-\w.()]/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, }; @@ -232,6 +350,9 @@ export class Constants { }; public static validationMessages = { + arrayElementsShouldBeValid: (elementsType: string) => { + return `Array elements should have valid value of type ${elementsType}`; + }, forbiddenChars: { dotAtTheEnd: "Input value must not have '.' at the end.", networkName: 'Invalid name. Name can contain only lowercase letters and numbers.', @@ -266,8 +387,18 @@ export class Constants { projectIdAlreadyExists: 'Network with project ID already exists.', resourceGroupAlreadyExists: Constants.getMessageResourceGroupAlreadyExist, 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 = { @@ -277,6 +408,7 @@ export class Constants { generateMnemonic: 'Generate mnemonic', pasteMnemonic: 'Paste mnemonic', resourceGroupName: 'Resource Group Name', + selectBlockchainDataManagerInstance: 'Select Blockchain Data Manager instance', selectConsortium: 'Select consortium', selectDeployDestination: 'Select deploy destination', selectDestination: 'Select destination', @@ -294,76 +426,6 @@ export class Constants { setupMnemonic: 'Setup mnemonic', }; - public static treeItemData = { - member: { - azure: { - contextValue: 'member', - iconPath: { dark: '', light: ''}, - }, - }, - network: { - azure: { - contextValue: 'network', - 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: ''}, - }, - 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', - }, - 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', - }, - }, - }; - // 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 } = { @@ -444,7 +506,7 @@ export class Constants { AstIsEmpty: 'enums could not be extracted, current AST is empty', BuildContractsBeforeGenerating: 'Please build contracts before generating', BuildContractsDirIsEmpty: Constants.getMessageContractsBuildDirectoryIsEmpty, - BuildContractsDirIsNotExist: Constants.getMessageContractsBuildDirectoryIsNotExist, + BuildContractsDirDoesNotExist: Constants.getMessageContractsBuildDirectoryDoesNotExist, CompiledContractIsMissing: 'Compiled contract is missing for solidity file.', DirectoryIsNotEmpty: 'Directory is not empty. Open another one?', ErrorWhileExecutingCommand: 'Error while executing command: ', @@ -543,19 +605,32 @@ export class Constants { 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'}, + LogicApp: { label: 'Logic App', serviceType: 1, outputDir: 'generatedLogicApp' }, + }; + + public static azureApiVersions = { + preview20180601: '2018-06-01-preview', + preview20190601: '2019-06-01-preview', }; public static azureResourceExplorer = { contentType: 'application/json', - portalBasUri: 'https://portal.azure.com/#@microsoft.onmicrosoft.com', + portalBasUri: 'https://ms.portal.azure.com/#@microsoft.onmicrosoft.com', + portalBladeUri: 'https://ms.portal.azure.com/#blade/ManagedLedgerExtension/TransactionNodeMenuBlade', providerName: 'Microsoft.Blockchain', requestAcceptLanguage: 'en-US', - requestApiVersion: '2018-06-01-preview', requestBaseUri: 'https://management.azure.com', resourceType: 'blockchainMembers', }; + 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 ozVersionUserSettingsKey = 'azureBlockchainService.openZeppelin.version'; @@ -563,6 +638,9 @@ export class Constants { 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', @@ -574,8 +652,10 @@ export class Constants { 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}`; @@ -588,8 +668,8 @@ export class Constants { }, 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 ? '...' : ''); + + existing.slice(0, 3).map((contract) => contract.name).join(' ') + + (existing.length > 3 ? '...' : ''); }, invalidHashMessage(contractPath: string): string { return `${contractPath} - invalid hash`; @@ -608,7 +688,7 @@ export class Constants { }, 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})?`; + `Do you want to use the latest one (${lastVersion})?`; }, }; @@ -623,11 +703,21 @@ export class Constants { this.infuraFileResponse.path = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'index.html')); this.infuraFileResponse.css = context.asAbsolutePath(path.join('resources', 'codeFlowResult', 'main.css')); - this.treeItemData.member.azure.iconPath = { + 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')), @@ -638,6 +728,21 @@ export class Constants { 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')), @@ -653,6 +758,11 @@ export class Constants { 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')), @@ -668,6 +778,11 @@ export class Constants { 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')), @@ -697,8 +812,8 @@ export class Constants { return `Contracts build directory "${buildDirPath}" is empty.`; } - private static getMessageContractsBuildDirectoryIsNotExist(buildDirPath: string): string { - return `Contracts build directory "${buildDirPath}" is not exist.`; + private static getMessageContractsBuildDirectoryDoesNotExist(buildDirPath: string): string { + return `Contracts build directory "${buildDirPath}" does not exist.`; } private static getMessageNetworkAlreadyExist(networkName: string): string { diff --git a/src/Generators/LogicAppGenerator.ts b/src/Generators/LogicAppGenerator.ts index 33c6376..a42c148 100644 --- a/src/Generators/LogicAppGenerator.ts +++ b/src/Generators/LogicAppGenerator.ts @@ -81,10 +81,10 @@ export class LogicAppGenerator { const files: string[] = []; if (!fs.pathExistsSync(buildDir)) { - Telemetry.sendException(new Error(Constants.errorMessageStrings.BuildContractsDirIsNotExist( + Telemetry.sendException(new Error(Constants.errorMessageStrings.BuildContractsDirDoesNotExist( Telemetry.obfuscate(buildDir), ))); - throw new Error(Constants.errorMessageStrings.BuildContractsDirIsNotExist(buildDir)); + throw new Error(Constants.errorMessageStrings.BuildContractsDirDoesNotExist(buildDir)); } if (filePath) { diff --git a/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerInputAndOutputItemCreator.ts b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerInputAndOutputItemCreator.ts new file mode 100644 index 0000000..891cfe2 --- /dev/null +++ b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerInputAndOutputItemCreator.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ItemType } from '../../ItemType'; +import { BlockchainDataManagerInputAndOutput } from '../../TreeItems/BlockchainDataManager/BlockchainDataManagerInputAndOutput'; +import { ItemCreator } from '../ItemCreator'; + +export class BlockchainDataManagerInputAndOutputItemCreator extends ItemCreator { + protected getRequiredFields(): Array<{ fieldName: string, type: string }> { + const requiredFields = super.getRequiredFields(); + requiredFields.push(...[ + { fieldName: 'label', type: 'string' }, + ]); + + return requiredFields; + } + + protected getAdditionalConstructorArguments(obj: { [key: string]: any }): any[] { + return [ + ...super.getAdditionalConstructorArguments(obj), + obj.label, + obj.itemType, + ]; + } + + protected createFromObject( + label: string, + itemType: ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP | ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP, + ): BlockchainDataManagerInputAndOutput { + return new BlockchainDataManagerInputAndOutput(itemType, label); + } +} diff --git a/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerNetworkNodeItemCreator.ts b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerNetworkNodeItemCreator.ts new file mode 100644 index 0000000..03459d0 --- /dev/null +++ b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerNetworkNodeItemCreator.ts @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ItemType } from '../../ItemType'; +import { BlockchainDataManagerNetworkNode } from '../../TreeItems'; +import { NetworkNodeItemCreator } from '../NetworkNodeItemCreator'; + +export class BlockchainDataManagerNetworkNodeItemCreator extends NetworkNodeItemCreator { + protected getRequiredFields(): Array<{ fieldName: string, type: string }> { + const requiredFields = super.getRequiredFields(); + requiredFields.push(...[ + { fieldName: 'subscriptionId', type: 'string' }, + { fieldName: 'resourceGroup', type: 'string' }, + ]); + + return requiredFields; + } + + protected getAdditionalConstructorArguments(obj: { [key: string]: any }): any[] { + return [ + ...super.getAdditionalConstructorArguments(obj), + obj.subscriptionId, + obj.resourceGroup, + obj.itemType, + ]; + } + + protected createFromObject( + label: string, + url: string, + networkId: string, + subscriptionId: string, + resourceGroup: string, + itemType: ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION | + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT | + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT, + ): BlockchainDataManagerNetworkNode { + return new BlockchainDataManagerNetworkNode(label, networkId, subscriptionId, resourceGroup, itemType, url); + } +} diff --git a/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerProjectItemCreator.ts b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerProjectItemCreator.ts new file mode 100644 index 0000000..9da3490 --- /dev/null +++ b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerProjectItemCreator.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { BlockchainDataManagerProject } from '../../TreeItems'; +import { ItemCreator } from '../ItemCreator'; + +export class BlockchainDataManagerProjectItemCreator extends ItemCreator { + protected getRequiredFields(): Array<{ fieldName: string, type: string }> { + const requiredFields = super.getRequiredFields(); + requiredFields.push(...[ + { fieldName: 'label', type: 'string' }, + { fieldName: 'subscriptionId', type: 'string' }, + { fieldName: 'resourceGroup', type: 'string' }, + ]); + + return requiredFields; + } + + protected getAdditionalConstructorArguments(obj: { [key: string]: any }): any[] { + return [ + obj.label, + obj.subscriptionId, + obj.resourceGroup, + ]; + } + + protected createFromObject( + label: string, + subscriptionId: string, + resourceGroup: string, + ): BlockchainDataManagerProject { + return new BlockchainDataManagerProject(label, subscriptionId, resourceGroup); + } +} diff --git a/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerServiceItemCreator.ts b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerServiceItemCreator.ts new file mode 100644 index 0000000..acea4af --- /dev/null +++ b/src/Models/ItemCreators/BlockchainDataManager/BlockchainDataManagerServiceItemCreator.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { BlockchainDataManagerService } from '../../TreeItems'; +import { ItemCreator } from '../ItemCreator'; + +export class BlockchainDataManagerServiceItemCreator extends ItemCreator { + protected createFromObject(): BlockchainDataManagerService { + return new BlockchainDataManagerService(); + } +} diff --git a/src/Models/ItemCreators/index.ts b/src/Models/ItemCreators/index.ts index 816b72d..776b7c5 100644 --- a/src/Models/ItemCreators/index.ts +++ b/src/Models/ItemCreators/index.ts @@ -4,6 +4,10 @@ export * from './AzureBlockchainNetworkNodeItemCreator'; export * from './AzureBlockchainProjectItemCreator'; export * from './AzureBlockchainServiceItemCreator'; +export * from './BlockchainDataManager/BlockchainDataManagerNetworkNodeItemCreator'; +export * from './BlockchainDataManager/BlockchainDataManagerProjectItemCreator'; +export * from './BlockchainDataManager/BlockchainDataManagerServiceItemCreator'; +export * from './BlockchainDataManager/BlockchainDataManagerInputAndOutputItemCreator'; export * from './CommandItemCreator'; export * from './InfuraNetworkNodeItemCreator'; export * from './InfuraProjectItemCreator'; diff --git a/src/Models/ItemType.ts b/src/Models/ItemType.ts index 56b7f48..21293b0 100644 --- a/src/Models/ItemType.ts +++ b/src/Models/ItemType.ts @@ -21,4 +21,12 @@ export enum ItemType { INFURA_SERVICE = 40, INFURA_PROJECT = 41, INFURA_NETWORK_NODE = 42, + BLOCKCHAIN_DATA_MANAGER_SERVICE = 50, + BLOCKCHAIN_DATA_MANAGER_PROJECT = 51, + BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP = 52, + BLOCKCHAIN_DATA_MANAGER_INPUT = 53, + BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP = 54, + BLOCKCHAIN_DATA_MANAGER_OUTPUT = 55, + BLOCKCHAIN_DATA_MANAGER_APPLICATION = 56, + } diff --git a/src/Models/QuickPickItems/BlockchainDataManagerInstanceItem.ts b/src/Models/QuickPickItems/BlockchainDataManagerInstanceItem.ts new file mode 100644 index 0000000..f07d77c --- /dev/null +++ b/src/Models/QuickPickItems/BlockchainDataManagerInstanceItem.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { QuickPickItem } from 'vscode'; + +export class BlockchainDataManagerInstanceItem implements QuickPickItem { + public readonly bdmName: string; + public readonly uniqueId: string; + public readonly resourceGroup: string; + public readonly subscriptionId: string; + public readonly label: string; + + constructor(bdmName: string, subscriptionId: string, resourceGroup: string, uniqueId: string) { + this.bdmName = bdmName; + this.subscriptionId = subscriptionId; + this.resourceGroup = resourceGroup; + this.uniqueId = uniqueId; + + this.label = bdmName; + } +} diff --git a/src/Models/QuickPickItems/index.ts b/src/Models/QuickPickItems/index.ts index aafc00d..beb94e9 100644 --- a/src/Models/QuickPickItems/index.ts +++ b/src/Models/QuickPickItems/index.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +export * from './BlockchainDataManagerInstanceItem'; export * from './ConsortiumItem'; export * from './InfuraProjectItem'; export * from './LocationItem'; diff --git a/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerInputAndOutput.ts b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerInputAndOutput.ts new file mode 100644 index 0000000..6496be0 --- /dev/null +++ b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerInputAndOutput.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Constants } from '../../../Constants'; +import { ItemType } from '../../ItemType'; +import { ExtensionItemData } from '../ExtensionItem'; +import { Group } from '../Group'; + +const { input, output } = Constants.treeItemData.group.bdm; + +export class BlockchainDataManagerInputAndOutput extends Group { + constructor( + itemType: ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP | ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP, + label: string, + ) { + const data: ExtensionItemData = itemType === ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP ? input : output; + + super(itemType, label, data); + } +} diff --git a/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerNetworkNode.ts b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerNetworkNode.ts new file mode 100644 index 0000000..c32cf72 --- /dev/null +++ b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerNetworkNode.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Constants } from '../../../Constants'; +import { ItemType } from '../../ItemType'; +import { ExtensionItemData } from '../ExtensionItem'; +import { NetworkNode } from '../NetworkNode'; + +const { application, input, output } = Constants.treeItemData.network.bdm; + +export class BlockchainDataManagerNetworkNode extends NetworkNode { + public readonly subscriptionId: string; + public readonly resourceGroup: string; + + constructor( + label: string, + networkId: number | string, + subscriptionId: string, + resourceGroup: string, + itemType: ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION | + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT | + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT, + url: string, + ) { + const data: ExtensionItemData = + itemType === ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION ? application : + itemType === ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT ? input : output; + + super( + itemType, + label, + data, + url, + networkId, + ); + + this.subscriptionId = subscriptionId; + this.resourceGroup = resourceGroup; + } + + public toJSON(): { [key: string]: any } { + const obj = super.toJSON(); + + obj.subscriptionId = this.subscriptionId; + obj.resourceGroup = this.resourceGroup; + + return obj; + } + + protected async getGasPrice(): Promise { + return 0; + } + + protected async getGasLimit(): Promise { + return 0; + } + + protected defaultProtocol(): string { + return Constants.networkProtocols.http; + } +} diff --git a/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerProject.ts b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerProject.ts new file mode 100644 index 0000000..82ee9b6 --- /dev/null +++ b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerProject.ts @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Constants } from '../../../Constants'; +import { IDeployDestination } from '../../IDeployDestination'; +import { ItemType } from '../../ItemType'; +import { Project } from '../Project'; + +export class BlockchainDataManagerProject extends Project { + public readonly subscriptionId: string; + public readonly resourceGroup: string; + + constructor(label: string, subscriptionId: string, resourceGroup: string) { + super( + ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT, + label, + Constants.treeItemData.project.bdm, + ); + + this.subscriptionId = subscriptionId; + this.resourceGroup = resourceGroup; + } + + public toJSON(): { [p: string]: any } { + const obj = super.toJSON(); + + obj.subscriptionId = this.subscriptionId; + obj.resourceGroup = this.resourceGroup; + + return obj; + } + + public getDeployDestinations(): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerService.ts b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerService.ts new file mode 100644 index 0000000..d6b14d3 --- /dev/null +++ b/src/Models/TreeItems/BlockchainDataManager/BlockchainDataManagerService.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Constants } from '../../../Constants'; +import { ItemType } from '../../ItemType'; +import { Service } from '../Service'; + +export class BlockchainDataManagerService extends Service { + constructor() { + super( + ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE, + Constants.treeItemData.service.bdm.label, + Constants.treeItemData.service.bdm, + ); + } +} diff --git a/src/Models/TreeItems/Group.ts b/src/Models/TreeItems/Group.ts new file mode 100644 index 0000000..23d6a89 --- /dev/null +++ b/src/Models/TreeItems/Group.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ItemType } from '../ItemType'; +import { ExtensionItem, ExtensionItemData } from './ExtensionItem'; + +export type GroupTypes = + ItemType.MEMBER | + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP | + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP; + +export abstract class Group extends ExtensionItem { + protected constructor(itemType: GroupTypes, label: string, data: ExtensionItemData) { + super(itemType, label, data); + } +} diff --git a/src/Models/TreeItems/Member.ts b/src/Models/TreeItems/Member.ts index 30b1faf..80e6770 100644 --- a/src/Models/TreeItems/Member.ts +++ b/src/Models/TreeItems/Member.ts @@ -3,10 +3,10 @@ import { Constants } from '../../Constants'; import { ItemType } from '../ItemType'; -import { ExtensionItem } from './ExtensionItem'; +import { Group } from './Group'; -export class Member extends ExtensionItem { +export class Member extends Group { constructor(label: string) { - super(ItemType.MEMBER, label, Constants.treeItemData.member.azure); + super(ItemType.MEMBER, label, Constants.treeItemData.group.azure.member); } } diff --git a/src/Models/TreeItems/NetworkNode.ts b/src/Models/TreeItems/NetworkNode.ts index 77a8e7a..e8a90a2 100644 --- a/src/Models/TreeItems/NetworkNode.ts +++ b/src/Models/TreeItems/NetworkNode.ts @@ -17,7 +17,10 @@ const protocolRegExp = new RegExp('^(' + export type NetworkNodeTypes = ItemType.AZURE_BLOCKCHAIN_NETWORK_NODE | ItemType.LOCAL_NETWORK_NODE | - ItemType.INFURA_NETWORK_NODE; + ItemType.INFURA_NETWORK_NODE | + ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION | + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT | + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT; export abstract class NetworkNode extends ExtensionItem { public readonly networkId: number | string; diff --git a/src/Models/TreeItems/Project.ts b/src/Models/TreeItems/Project.ts index 9b1628e..0e245b6 100644 --- a/src/Models/TreeItems/Project.ts +++ b/src/Models/TreeItems/Project.ts @@ -6,7 +6,11 @@ import { ItemType } from '../ItemType'; import { ExtensionItem, ExtensionItemData } from './ExtensionItem'; import { NetworkNode } from './NetworkNode'; -export type ProjectTypes = ItemType.AZURE_BLOCKCHAIN_PROJECT | ItemType.LOCAL_PROJECT | ItemType.INFURA_PROJECT; +export type ProjectTypes = + ItemType.AZURE_BLOCKCHAIN_PROJECT | + ItemType.LOCAL_PROJECT | + ItemType.INFURA_PROJECT | + ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT; export abstract class Project extends ExtensionItem { protected constructor(itemType: ProjectTypes, label: string, data: ExtensionItemData) { diff --git a/src/Models/TreeItems/Service.ts b/src/Models/TreeItems/Service.ts index 511dfe6..8eafa69 100644 --- a/src/Models/TreeItems/Service.ts +++ b/src/Models/TreeItems/Service.ts @@ -8,6 +8,7 @@ export type ServiceTypes = ItemType.AZURE_BLOCKCHAIN_SERVICE | ItemType.LOCAL_SERVICE | ItemType.INFURA_SERVICE | + ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE | ItemType.COMMAND; export abstract class Service extends ExtensionItem { diff --git a/src/Models/TreeItems/index.ts b/src/Models/TreeItems/index.ts index 0de614f..ec43018 100644 --- a/src/Models/TreeItems/index.ts +++ b/src/Models/TreeItems/index.ts @@ -4,8 +4,13 @@ export * from './AzureBlockchainNetworkNode'; export * from './AzureBlockchainProject'; export * from './AzureBlockchainService'; +export * from './BlockchainDataManager/BlockchainDataManagerNetworkNode'; +export * from './BlockchainDataManager/BlockchainDataManagerProject'; +export * from './BlockchainDataManager/BlockchainDataManagerService'; +export * from './BlockchainDataManager/BlockchainDataManagerInputAndOutput'; export * from './Command'; export * from './ExtensionItem'; +export * from './Group'; export * from './IExtensionItem'; export * from './InfuraNetworkNode'; export * from './InfuraProject'; diff --git a/src/Models/index.ts b/src/Models/index.ts index 0d8be0d..3ef7893 100644 --- a/src/Models/index.ts +++ b/src/Models/index.ts @@ -10,6 +10,10 @@ import { AzureBlockchainNetworkNodeItemCreator, AzureBlockchainProjectItemCreator, AzureBlockchainServiceItemCreator, + BlockchainDataManagerInputAndOutputItemCreator, + BlockchainDataManagerNetworkNodeItemCreator, + BlockchainDataManagerProjectItemCreator, + BlockchainDataManagerServiceItemCreator, CommandItemCreator, InfuraNetworkNodeItemCreator, InfuraProjectItemCreator, @@ -29,13 +33,24 @@ ItemFactory.register(ItemType.NULLABLE, new NullableItemCreator()); ItemFactory.register(ItemType.AZURE_BLOCKCHAIN_SERVICE, new AzureBlockchainServiceItemCreator()); ItemFactory.register(ItemType.LOCAL_SERVICE, new LocalServiceItemCreator()); ItemFactory.register(ItemType.INFURA_SERVICE, new InfuraServiceItemCreator()); +ItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE, new BlockchainDataManagerServiceItemCreator()); ItemFactory.register(ItemType.AZURE_BLOCKCHAIN_PROJECT, new AzureBlockchainProjectItemCreator()); ItemFactory.register(ItemType.LOCAL_PROJECT, new LocalProjectItemCreator()); ItemFactory.register(ItemType.INFURA_PROJECT, new InfuraProjectItemCreator()); +ItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT, new BlockchainDataManagerProjectItemCreator()); ItemFactory.register(ItemType.AZURE_BLOCKCHAIN_NETWORK_NODE, new AzureBlockchainNetworkNodeItemCreator()); ItemFactory.register(ItemType.LOCAL_NETWORK_NODE, new LocalNetworkNodeItemCreator()); ItemFactory.register(ItemType.INFURA_NETWORK_NODE, new InfuraNetworkNodeItemCreator()); +ItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION, new BlockchainDataManagerNetworkNodeItemCreator()); +ItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT, new BlockchainDataManagerNetworkNodeItemCreator()); +ItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT, new BlockchainDataManagerNetworkNodeItemCreator()); ItemFactory.register(ItemType.MEMBER, new MemberItemCreator()); +ItemFactory.register( + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP, + new BlockchainDataManagerInputAndOutputItemCreator()); +ItemFactory.register( + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP, + new BlockchainDataManagerInputAndOutputItemCreator()); diff --git a/src/ViewItems/GroupView.ts b/src/ViewItems/GroupView.ts new file mode 100644 index 0000000..37c19fd --- /dev/null +++ b/src/ViewItems/GroupView.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Group } from '../Models/TreeItems/Group'; +import { ExtensionView } from './ExtensionView'; + +export class GroupView extends ExtensionView { + constructor(groupItem: Group) { + super(groupItem); + } +} diff --git a/src/ViewItems/MemberView.ts b/src/ViewItems/MemberView.ts deleted file mode 100644 index 14d381b..0000000 --- a/src/ViewItems/MemberView.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { Member } from '../Models/TreeItems/Member'; -import { ExtensionView } from './ExtensionView'; - -export class MemberView extends ExtensionView { - constructor(memberItem: Member) { - super(memberItem); - } -} diff --git a/src/ViewItems/ViewCreators/GroupViewCreator.ts b/src/ViewItems/ViewCreators/GroupViewCreator.ts new file mode 100644 index 0000000..f6b67d1 --- /dev/null +++ b/src/ViewItems/ViewCreators/GroupViewCreator.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Group } from '../../Models/TreeItems'; +import { GroupView } from '../GroupView'; +import { ViewCreator } from './ViewCreator'; + +export class GroupViewCreator extends ViewCreator { + public create(groupItem: Group): GroupView { + return new GroupView(groupItem); + } +} diff --git a/src/ViewItems/ViewCreators/MemberViewCreator.ts b/src/ViewItems/ViewCreators/MemberViewCreator.ts deleted file mode 100644 index 49dd063..0000000 --- a/src/ViewItems/ViewCreators/MemberViewCreator.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { Member } from '../../Models/TreeItems'; -import { MemberView } from '../MemberView'; -import { ViewCreator } from './ViewCreator'; - -export class MemberViewCreator extends ViewCreator { - public create(memberItem: Member): MemberView { - return new MemberView(memberItem); - } -} diff --git a/src/ViewItems/ViewCreators/index.ts b/src/ViewItems/ViewCreators/index.ts index f8da9b4..bcf851e 100644 --- a/src/ViewItems/ViewCreators/index.ts +++ b/src/ViewItems/ViewCreators/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -export * from './MemberViewCreator'; +export * from './GroupViewCreator'; export * from './NetworkNodeViewCreator'; export * from './NullableViewCreator'; export * from './ProjectViewCreator'; diff --git a/src/ViewItems/index.ts b/src/ViewItems/index.ts index fecc8fd..786c78b 100644 --- a/src/ViewItems/index.ts +++ b/src/ViewItems/index.ts @@ -3,7 +3,7 @@ export * from './ExtensionView'; export * from './IExtensionView'; -export * from './MemberView'; +export * from './GroupView'; export * from './NetworkNodeView'; export * from './NullableView'; export * from './ProjectView'; @@ -12,7 +12,7 @@ export * from './ViewItemFactory'; import { ItemType } from '../Models'; import { - MemberViewCreator, + GroupViewCreator, NetworkNodeViewCreator, NullableViewCreator, ProjectViewCreator, @@ -26,13 +26,20 @@ ViewItemFactory.register(ItemType.NULLABLE, new NullableViewCreator()); ViewItemFactory.register(ItemType.AZURE_BLOCKCHAIN_SERVICE, new ServiceViewCreator()); ViewItemFactory.register(ItemType.LOCAL_SERVICE, new ServiceViewCreator()); ViewItemFactory.register(ItemType.INFURA_SERVICE, new ServiceViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE, new ServiceViewCreator()); ViewItemFactory.register(ItemType.AZURE_BLOCKCHAIN_PROJECT, new ProjectViewCreator()); ViewItemFactory.register(ItemType.LOCAL_PROJECT, new ProjectViewCreator()); ViewItemFactory.register(ItemType.INFURA_PROJECT, new ProjectViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT, new ProjectViewCreator()); ViewItemFactory.register(ItemType.AZURE_BLOCKCHAIN_NETWORK_NODE, new NetworkNodeViewCreator()); ViewItemFactory.register(ItemType.LOCAL_NETWORK_NODE, new NetworkNodeViewCreator()); ViewItemFactory.register(ItemType.INFURA_NETWORK_NODE, new NetworkNodeViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION, new NetworkNodeViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT, new NetworkNodeViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT, new NetworkNodeViewCreator()); -ViewItemFactory.register(ItemType.MEMBER, new MemberViewCreator()); +ViewItemFactory.register(ItemType.MEMBER, new GroupViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP, new GroupViewCreator()); +ViewItemFactory.register(ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP, new GroupViewCreator()); diff --git a/src/commands/OpenZeppelinCommands.ts b/src/commands/OpenZeppelinCommands.ts index 561de1c..246e57f 100644 --- a/src/commands/OpenZeppelinCommands.ts +++ b/src/commands/OpenZeppelinCommands.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import open = require('open'); +import * as open from 'open'; import { ProgressLocation, window } from 'vscode'; import { Constants } from '../Constants'; import { openZeppelinHelper, showQuickPick } from '../helpers'; @@ -36,11 +36,16 @@ export namespace OpenZeppelinCommands { const downloadedAssets = await downloadOZFiles(baseUrl, assetsStatuses.existing, assetsStatuses.missing, fullAssetWithDependencies); - await OpenZeppelinService.updateProjectJsonAsync(manifest.getVersion(), category, downloadedAssets); + const mergedAssets = await OpenZeppelinService.mergeAssetsWithExisting(downloadedAssets); + + await OpenZeppelinService.updateProjectJsonAsync(manifest.getVersion(), category, mergedAssets); openDocumentationUrl(manifest.getCategoryApiDocumentationUrl(category)); Telemetry.sendEvent('OpenZeppelinCommands.addCategory.generateMigrations'); + + await openZeppelinHelper.defineContractRequiredParameters(); + await OpenZeppelinMigrationsService.generateMigrations(await OpenZeppelinService.getAllDownloadedAssetsAsync()); } } diff --git a/src/commands/ServiceCommands.ts b/src/commands/ServiceCommands.ts index 0a3367b..0a940c7 100644 --- a/src/commands/ServiceCommands.ts +++ b/src/commands/ServiceCommands.ts @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import * as open from 'open'; import { Constants } from '../Constants'; import { showQuickPick } from '../helpers'; import { ItemType } from '../Models'; import { AzureBlockchainProject, AzureBlockchainService, + BlockchainDataManagerProject, + BlockchainDataManagerService, InfuraProject, InfuraService, LocalProject, @@ -15,10 +18,15 @@ import { Service, ServiceTypes, } from '../Models/TreeItems'; -import { ConsortiumResourceExplorer, InfuraResourceExplorer, LocalResourceExplorer } from '../resourceExplorers'; +import { + BlockchainDataManagerResourceExplorer, + ConsortiumResourceExplorer, + InfuraResourceExplorer, + LocalResourceExplorer, +} from '../resourceExplorers'; import { GanacheService, TreeManager } from '../services'; import { Telemetry } from '../TelemetryClient'; -import { ProjectView } from '../ViewItems'; +import { NetworkNodeView, ProjectView } from '../ViewItems'; interface IServiceDestination { cmd: (service: Service) => Promise; @@ -71,6 +79,11 @@ export namespace ServiceCommands { itemType: ItemType.INFURA_SERVICE, label: Constants.treeItemData.service.infura.label, }, + { + cmd: connectBlockchainDataManagerProject, + itemType: ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE, + label: Constants.treeItemData.service.bdm.label, + }, ]; const project = await execute(serviceDestinations); @@ -93,6 +106,10 @@ export namespace ServiceCommands { await TreeManager.removeItem(viewItem.extensionItem); Telemetry.sendEvent('ServiceCommands.disconnectProject.commandFinished'); } + + export function openAtAzurePortal(viewItem: NetworkNodeView): void { + open(viewItem.extensionItem.url.href); + } } async function execute(serviceDestinations: IServiceDestination[]): Promise { @@ -180,3 +197,16 @@ async function getExistingPorts(service: LocalService): Promise { const localProjects = service.getChildren() as LocalProject[]; return localProjects.map((item) => item.port); } + +// ------------ BLOCKCHAIN DATA MANAGER ------------ // + +async function connectBlockchainDataManagerProject(service: BlockchainDataManagerService) +: Promise { + const bdmResourceExplorer = new BlockchainDataManagerResourceExplorer(); + return bdmResourceExplorer.selectProject(await getExistingBlockchainDataManager(service)); +} + +async function getExistingBlockchainDataManager(service: BlockchainDataManagerService): Promise { + const bdmProjects = service.getChildren() as BlockchainDataManagerProject[]; + return bdmProjects.map((item) => item.label); +} diff --git a/src/commands/TruffleCommands.ts b/src/commands/TruffleCommands.ts index b4cbe18..59f982c 100644 --- a/src/commands/TruffleCommands.ts +++ b/src/commands/TruffleCommands.ts @@ -22,7 +22,7 @@ import { } from '../helpers'; import { IDeployDestination, ItemType } from '../Models'; import { NetworkForContractItem } from '../Models/QuickPickItems/NetworkForContractItem'; -import { LocalService } from '../Models/TreeItems'; +import { AzureBlockchainProject, InfuraProject, LocalProject, LocalService } from '../Models/TreeItems'; import { Project } from '../Models/TreeItems'; import { Output } from '../Output'; import { @@ -76,6 +76,8 @@ export namespace TruffleCommands { export async function deployContracts(): Promise { Telemetry.sendEvent('TruffleCommands.deployContracts.commandStarted'); + await checkOpenZeppelinIfUsed(); + const truffleConfigsUri = TruffleConfiguration.getTruffleConfigUri(); const defaultDeployDestinations = getDefaultDeployDestinations(truffleConfigsUri); const truffleDeployDestinations = await getTruffleDeployDestinations(truffleConfigsUri); @@ -101,7 +103,6 @@ export namespace TruffleCommands { { url: Telemetry.obfuscate(command.description || '') }, ); - await checkOpenZeppelinIfUsed(); await command.cmd(); Telemetry.sendEvent('TruffleCommands.deployContracts.commandFinished'); @@ -228,6 +229,7 @@ async function checkOpenZeppelinIfUsed(): Promise { } await validateOpenZeppelinContracts(); + await openZeppelinHelper.defineContractRequiredParameters(); } } @@ -279,7 +281,9 @@ async function validateOpenZeppelinContracts(): Promise { const errorMsg = Constants.validationMessages.openZeppelinFilesAreInvalid(invalidContractsPaths); const error = new Error(errorMsg); window.showErrorMessage(errorMsg); - Telemetry.sendException(error); + const obfuscatedPaths = invalidContractsPaths.map((invalidContractsPath) => + Telemetry.obfuscate(invalidContractsPath)); + Telemetry.sendException(new Error(Constants.validationMessages.openZeppelinFilesAreInvalid(obfuscatedPaths))); throw error; } } @@ -336,7 +340,12 @@ async function getProjectDeployDestinationItems(projects: Project[], truffleConf : Promise { const destinations: IDeployDestination[] = []; - for (const project of projects) { + const filteredProjects = projects.filter((project) => + project instanceof AzureBlockchainProject || + project instanceof InfuraProject || + project instanceof LocalProject); + + for (const project of filteredProjects) { const projectDestinations = await project.getDeployDestinations(); destinations.push(...projectDestinations); } @@ -361,8 +370,7 @@ async function getTruffleDeployFunction( port?: number) : Promise<() => Promise> { const treeProjectNames = await getTreeProjectNames(); - if (port !== undefined && - (treeProjectNames.includes(name) || name === Constants.localhostName)) { + if (port !== undefined && (treeProjectNames.includes(name) || name === Constants.localhostName)) { Telemetry.sendEvent('TruffleCommands.getTruffleDeployFunction.returnDeployToLocalGanache'); return deployToLocalGanache.bind(undefined, name, truffleConfigPath, port); } diff --git a/src/extension.ts b/src/extension.ts index e887349..31b6023 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -28,7 +28,7 @@ import { TreeService, } from './services'; import { Telemetry } from './TelemetryClient'; -import { ProjectView } from './ViewItems'; +import { NetworkNodeView, ProjectView } from './ViewItems'; import { DebuggerConfiguration } from './debugAdapter/configuration/debuggerConfiguration'; @@ -117,6 +117,8 @@ export async function activate(context: ExtensionContext) { async (viewItem: ProjectView) => { await tryExecute(() => ServiceCommands.disconnectProject(viewItem)); }); + const openAtAzurePortal = commands.registerCommand('azureBlockchainService.openAtAzurePortal', + async (viewItem: NetworkNodeView) => ServiceCommands.openAtAzurePortal(viewItem)); //#endregion //#region Infura commands @@ -131,8 +133,8 @@ export async function activate(context: ExtensionContext) { async () => { await tryExecute(() => InfuraCommands.showProjectsFromAccount()); }); - //#endregion + //#region contract commands const showSmartContractPage = commands.registerCommand( 'azureBlockchainService.showSmartContractPage', @@ -209,6 +211,7 @@ export async function activate(context: ExtensionContext) { signOutOfInfuraAccount, showProjectsFromInfuraAccount, openZeppelinAddCategory, + openAtAzurePortal, ]; context.subscriptions.push(...subscriptions); diff --git a/src/helpers/bigIntMath.ts b/src/helpers/bigIntMath.ts new file mode 100644 index 0000000..c99653f --- /dev/null +++ b/src/helpers/bigIntMath.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +export const bigIntMath = { + // eval required because babel translate ** to Math.pow + // tslint:disable-next-line:no-eval + pow: (value: BigInt, pow: number) => BigInt(eval(`BigInt(${value}) ** BigInt(${pow})`)), +}; diff --git a/src/helpers/checkTruffleConfigTemplate.js b/src/helpers/checkTruffleConfigTemplate.js index 006d7ca..00e761f 100644 --- a/src/helpers/checkTruffleConfigTemplate.js +++ b/src/helpers/checkTruffleConfigTemplate.js @@ -1,8 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -const maxLength = 4095; -const truffleConfig = require(process.argv[2]); +const path = require('path'); + +try { + const hdwalletNodeModulePath = path.join(process.cwd(), 'node_modules', 'truffle-hdwallet-provider'); + require(hdwalletNodeModulePath); + require.cache[require.resolve(hdwalletNodeModulePath)].exports = function HDWallet(...args) { + this.mnemonic = args[0]; + this.url = args[1]; + }; +} catch (err) { + // ignore +} + +const truffleConfig = require(path.join(process.cwd(), 'truffle-config.js')); const getCircularReplacer = () => { const seen = new WeakSet(); @@ -15,7 +27,11 @@ const getCircularReplacer = () => { } if (typeof v === 'function') { - return v.toString(); + if (k !== 'provider') { + return v.toString(); + } + + return v.call(truffleConfig); } return v; @@ -24,16 +40,4 @@ const getCircularReplacer = () => { let message = JSON.stringify(truffleConfig, getCircularReplacer()); -if (message.length > maxLength) { - for (let i = 0; i < message.length; i++) { - const index = Math.min(maxLength, message.length); - const str = message.substr(0, index); - message = message.substr(index); - - process.send({ command: 'truffleConfig', batch: { index: i, done: message.length === 0, message: str} }); - } -} else { - process.send({ command: 'truffleConfig', message: message }); -} - -setTimeout(() => process.exit(), 1000); +process.send({ command: 'truffleConfig', message: message }, () => process.exit()); diff --git a/src/helpers/openZeppelinHelper.ts b/src/helpers/openZeppelinHelper.ts index 75f41fe..774a6e0 100644 --- a/src/helpers/openZeppelinHelper.ts +++ b/src/helpers/openZeppelinHelper.ts @@ -2,13 +2,15 @@ // Licensed under the MIT license. import { ProgressLocation, window } from 'vscode'; -import { showConfirmDialogToUpdateOpenZeppelin, userSettings } from '.'; +import { showConfirmationDialog, showInputBox, userSettings } from '.'; import { Constants } from '../Constants'; -import { OpenZeppelinService } from '../services'; +import { CancellationEvent } from '../Models'; +import { OpenZeppelinMigrationsService, OpenZeppelinProjectJsonService, OpenZeppelinService } from '../services'; import { CurrentOpenZeppelinVersionLocation, InvalidOpenZeppelinVersionException } from '../services/openZeppelin/InvalidOpenZeppelinVersionException'; -import { IOZMetadata } from '../services/openZeppelin/models'; +import { IOZAsset, IOZMetadata } from '../services/openZeppelin/models'; import { OpenZeppelinManifest } from '../services/openZeppelin/OpenZeppelinManifest'; import { Telemetry } from '../TelemetryClient'; +import { validateSolidityType } from '../validators/solidityTypeValidation'; export async function createManifestAsync(version: string): Promise { const metadata = await getManifestMetadata(version); @@ -49,7 +51,7 @@ export async function shouldUpgradeOpenZeppelinAsync(): Promise { const currentVersion = await tryGetCurrentOpenZeppelinVersionAsync(); const latestVersion = await OpenZeppelinService.getLatestOpenZeppelinVersionAsync(); - return currentVersion !== latestVersion && await showConfirmDialogToUpdateOpenZeppelin(); + return currentVersion !== latestVersion && await showConfirmationDialog(Constants.openZeppelin.newVersionAvailable); } export async function upgradeOpenZeppelinUserSettingsAsync() { @@ -68,10 +70,98 @@ export async function upgradeOpenZeppelinContractsAsync() const latestVersion = await OpenZeppelinService.getLatestOpenZeppelinVersionAsync(); const manifest = await createManifestAsync(latestVersion); - return OpenZeppelinService.updateOpenZeppelinContractsAsync(manifest); + await OpenZeppelinService.updateOpenZeppelinContractsAsync(manifest); + await OpenZeppelinMigrationsService.generateMigrations(await OpenZeppelinService.getAllDownloadedAssetsAsync()); }); } +export async function defineContractRequiredParameters(): Promise { + const assetsWithNotSpecifiedRequiredParameters = await getAssetsWithNotSpecifiedRequiredParameters(); + + if (assetsWithNotSpecifiedRequiredParameters.length === 0) { + return; + } + + if (await showConfirmationDialog(Constants.openZeppelin.specifyContractParameters)) { + await setContractParameters(assetsWithNotSpecifiedRequiredParameters); + } + + await OpenZeppelinMigrationsService.generateMigrations(await OpenZeppelinService.getAllDownloadedAssetsAsync()); +} + function getManifestMetadata(version: string): IOZMetadata { return require(`../services/openZeppelin/manifest-${version}.json`) as IOZMetadata; } + +async function setContractParameters(assets: IOZAsset[]): Promise { + const projectMetadata = await OpenZeppelinProjectJsonService.getProjectJson(); + try { + for (const asset of assets) { + for (const [contractName, contractParameters] of Object.entries(asset.requiredParameters!)) { + for (const parameter of contractParameters) { + if (!parameter.value) { + parameter.value = await showInputBox({ + ignoreFocusOut: true, + prompt: Constants.openZeppelin.contactParameterInformation( + contractName, + parameter.name, + parameter.type, + ), + validateInput: (v) => validateSolidityType(v, parameter.type), + }); + } + } + } + } + await OpenZeppelinProjectJsonService.addAssetsToProjectJsonAsync( + assets, + projectMetadata); + await OpenZeppelinProjectJsonService.storeProjectJsonAsync(projectMetadata); + } catch (error) { + if (await showConfirmationDialog(Constants.openZeppelin.saveSpecifiedParameters)) { + await OpenZeppelinProjectJsonService.addAssetsToProjectJsonAsync( + assets, + projectMetadata); + await OpenZeppelinProjectJsonService.storeProjectJsonAsync(projectMetadata); + } + if (error instanceof CancellationEvent) { + return; + } else { + throw error; + } + } +} + +async function getAssetsWithNotSpecifiedRequiredParameters() { + const currentOZVersion = await tryGetCurrentOpenZeppelinVersionAsync(); + const manifest = await createManifestAsync(currentOZVersion); + const manifestAssets = manifest.getAssets(); + const currentAssets = await OpenZeppelinService.getAllDownloadedAssetsAsync(); + + return currentAssets.reduce((assets, asset) => { + const manifestAsset = manifestAssets.find((assetFromManifest) => assetFromManifest.id === asset.id); + if (manifestAsset && !!manifestAsset.requiredParameters) { + if (!!asset.requiredParameters) { + for (const [contractName, contractParameters] of Object.entries(asset.requiredParameters)) { + const manifestContractParameters = manifestAsset.requiredParameters[contractName]; + if (manifestContractParameters) { + for (const parameter of contractParameters) { + if (parameter.value === undefined) { + const param = manifestContractParameters.find((manifestContractParameter) => + manifestContractParameter.name === parameter.name && + manifestContractParameter.type === parameter.type); + if (param) { + assets.push(asset); + return assets; + } + } + } + } + } + } else { + assets.push(JSON.parse(JSON.stringify(manifestAsset))); + } + } + return assets; + }, [] as IOZAsset[]); +} diff --git a/src/helpers/truffleConfig.ts b/src/helpers/truffleConfig.ts index e8fa864..a46807a 100644 --- a/src/helpers/truffleConfig.ts +++ b/src/helpers/truffleConfig.ts @@ -276,10 +276,8 @@ export namespace TruffleConfiguration { async function getTruffleMetadata(): Promise { const truffleConfigTemplatePath = path.join(__dirname, 'checkTruffleConfigTemplate.js'); - const truffleConfigPath = - path.relative(path.dirname(truffleConfigTemplatePath), path.join(getWorkspaceRoot()!, 'truffle-config.js')); - const result = await tryExecuteCommandInFork(getWorkspaceRoot()!, truffleConfigTemplatePath, truffleConfigPath); + const result = await tryExecuteCommandInFork(getWorkspaceRoot()!, truffleConfigTemplatePath); const truffleConfigObject = result.messages!.find((message) => message.command === 'truffleConfig'); if (!truffleConfigObject || !truffleConfigObject.message) { @@ -512,9 +510,17 @@ export namespace TruffleConfiguration { const { contracts_directory, contracts_build_directory, migrations_directory } = Constants.truffleConfigDefaultDirectory; - truffleConfig.contracts_directory = truffleConfig.contracts_directory || contracts_directory; - truffleConfig.contracts_build_directory = truffleConfig.contracts_build_directory || contracts_build_directory; - truffleConfig.migrations_directory = truffleConfig.migrations_directory || migrations_directory; + if (!truffleConfig.hasOwnProperty('contracts_directory')) { + truffleConfig.contracts_directory = contracts_directory; + } + + if (!truffleConfig.hasOwnProperty('contracts_build_directory')) { + truffleConfig.contracts_build_directory = contracts_build_directory; + } + + if (!truffleConfig.hasOwnProperty('migrations_directory')) { + truffleConfig.migrations_directory = migrations_directory; + } const arrayNetwork: INetwork[] = []; diff --git a/src/helpers/userInteraction.ts b/src/helpers/userInteraction.ts index 139bc3a..209ff91 100644 --- a/src/helpers/userInteraction.ts +++ b/src/helpers/userInteraction.ts @@ -55,7 +55,7 @@ export async function showConfirmPaidOperationDialog() { validateInput: DialogResultValidator.validateConfirmationResult, }); - if (answer.toLowerCase() !== Constants.confirmationDialogResult.yes) { + if (answer.toLowerCase() !== Constants.confirmationDialogResult.yes.toLowerCase()) { Telemetry.sendEvent( 'userInteraction.showConfirmPaidOperationDialog.userCancellation', { prompt: Constants.placeholders.confirmPaidOperation }); @@ -117,11 +117,12 @@ export async function saveTextInFile( return file.fsPath; } -export async function showConfirmDialogToUpdateOpenZeppelin(): Promise { +export async function showConfirmationDialog(message: string): Promise { const answer = await window.showInformationMessage( - Constants.openZeppelin.newVersionAvailable, + message, Constants.confirmationDialogResult.yes, - Constants.confirmationDialogResult.no); + Constants.confirmationDialogResult.no, + ); return answer === Constants.confirmationDialogResult.yes; } diff --git a/src/resourceExplorers/BlockchainDataManagerResourceExplorer.ts b/src/resourceExplorers/BlockchainDataManagerResourceExplorer.ts new file mode 100644 index 0000000..8254057 --- /dev/null +++ b/src/resourceExplorers/BlockchainDataManagerResourceExplorer.ts @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { QuickPickItem } from 'vscode'; +import { AzureBlockchainServiceClient, IAzureBlockchainDataManagerDto } from '../ARMBlockchain'; +import { Constants } from '../Constants'; +import { showQuickPick } from '../helpers'; +import { ItemType } from '../Models/ItemType'; +import { BlockchainDataManagerInstanceItem, ResourceGroupItem, SubscriptionItem } from '../Models/QuickPickItems'; +import { BlockchainDataManagerInputAndOutput, BlockchainDataManagerNetworkNode, BlockchainDataManagerProject } from '../Models/TreeItems'; +import { Telemetry } from '../TelemetryClient'; +import { AzureResourceExplorer } from './AzureResourceExplorer'; + +export class BlockchainDataManagerResourceExplorer extends AzureResourceExplorer { + public async selectProject(existingProjects: string[] = []): Promise { + Telemetry.sendEvent('BlockchainDataManagerResourceExplorer.selectProject'); + await this.waitForLogin(); + + const subscriptionItem = await this.getOrSelectSubscriptionItem(); + const resourceGroupItem = await this.getOrCreateResourceGroupItem(subscriptionItem); + const azureClient = await this.getAzureClient(subscriptionItem, resourceGroupItem); + + const pick = await showQuickPick( + this.getBlockchainDataManagerInstanceItems(azureClient, subscriptionItem, resourceGroupItem, existingProjects), + { placeHolder: Constants.placeholders.selectBlockchainDataManagerInstance, ignoreFocusOut: true }, + ); + + if (pick instanceof BlockchainDataManagerInstanceItem) { + Telemetry.sendEvent('BlockchainDataManagerResourceExplorer.selectProject.selectBlockchainDataManagerProject'); + return this.getBlockchainDataManagerInstance(pick, azureClient); + } else { + Telemetry.sendEvent('BlockchainDataManagerResourceExplorer.selectProject.createBlockchainDataManagerProject'); + // TODO: implement createBlockchainDataManagerInstance + return Promise.resolve({} as BlockchainDataManagerProject); + } + } + + private async getBlockchainDataManagerInstanceItems( + azureClient: AzureBlockchainServiceClient, + subscriptionItem: SubscriptionItem, + resourceGroupItem: ResourceGroupItem, + excludedItems: string[] = []) + : Promise { + + const items: QuickPickItem[] = []; + + // TODO: add logic for create BlockchainDataManager project + const bdmItems = + await this.loadBlockchainDataManagerInstanceItem(azureClient, subscriptionItem, resourceGroupItem, excludedItems); + + items.push(...bdmItems); + + return items; + } + + private async loadBlockchainDataManagerInstanceItem( + azureClient: AzureBlockchainServiceClient, + subscriptionItem: SubscriptionItem, + resourceGroupItem: ResourceGroupItem, + excludedItems: string[] = []) + : Promise { + const bdmItems: IAzureBlockchainDataManagerDto[] = await azureClient.bdmResource.getListBlockchainDataManager(); + + return bdmItems + .filter((item) => !excludedItems.includes(item.name)) + .map((item) => new BlockchainDataManagerInstanceItem( + item.name, + subscriptionItem.subscriptionId, + resourceGroupItem.label, + item.properties.uniqueId, + )) + .sort((a, b) => a.label.localeCompare(b.label)); + } + + private async getBlockchainDataManagerInstance( + bdmInstanceItem: BlockchainDataManagerInstanceItem, + azureClient: AzureBlockchainServiceClient) + : Promise { + const { bdmName, subscriptionId, resourceGroup } = bdmInstanceItem; + + const bdmProject = new BlockchainDataManagerProject(bdmName, subscriptionId, resourceGroup); + const bdmApplications = await this.getBlockchainDataManagerApplications(bdmInstanceItem, azureClient); + const bdmInputs = await this.getBlockchainDataManagerInputs(bdmInstanceItem, azureClient); + const bdmOutputs = await this.getBlockchainDataManagerOutputs(bdmInstanceItem, azureClient); + + bdmProject.setChildren([...bdmApplications, bdmInputs, bdmOutputs]); + + return bdmProject; + } + + private async getBlockchainDataManagerApplications( + bdmInstanceItem: BlockchainDataManagerInstanceItem, + azureClient: AzureBlockchainServiceClient) + : Promise { + const { bdmName, subscriptionId, resourceGroup } = bdmInstanceItem; + + const listBDMApplication = await azureClient.bdmResource.getListBlockchainDataManagerApplication(bdmName); + + return listBDMApplication.map((applicaion) => { + const indexRemovingRoute = applicaion.id.lastIndexOf('/artifacts'); + const url = `${Constants.azureResourceExplorer.portalBasUri}/resource${applicaion.id.slice(0, indexRemovingRoute)}/blockchainapplications`; + + return new BlockchainDataManagerNetworkNode( + applicaion.name, + '*', + subscriptionId, + resourceGroup, + ItemType.BLOCKCHAIN_DATA_MANAGER_APPLICATION, + url); + }); + } + + private async getBlockchainDataManagerInputs( + bdmInstanceItem: BlockchainDataManagerInstanceItem, + azureClient: AzureBlockchainServiceClient) + : Promise { + const { bdmName, subscriptionId, resourceGroup } = bdmInstanceItem; + + const listBDMInput = await azureClient.bdmResource.getListBlockchainDataManagerInput(bdmName); + + const bdmInputs = listBDMInput.map((input) => { + return new BlockchainDataManagerNetworkNode( + input.name, + '*', + subscriptionId, + resourceGroup, + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT, + this.getInputUrl(input.properties.dataSource.resourceId)); + }); + + const inputs = new BlockchainDataManagerInputAndOutput( + ItemType.BLOCKCHAIN_DATA_MANAGER_INPUT_GROUP, + Constants.treeItemData.group.bdm.input.label, + ); + + inputs.setChildren(bdmInputs); + + return inputs; + } + + private async getBlockchainDataManagerOutputs( + bdmInstanceItem: BlockchainDataManagerInstanceItem, + azureClient: AzureBlockchainServiceClient) + : Promise { + const { bdmName, subscriptionId, resourceGroup } = bdmInstanceItem; + + const listBDMOutput = await azureClient.bdmResource.getListBlockchainDataManagerOutput(bdmName); + + const bdmOutputs = listBDMOutput.map((output) => { + const indexRemovingRoute = output.id.lastIndexOf('/outputs'); + const url = `${Constants.azureResourceExplorer.portalBasUri}/resource${output.id.slice(0, indexRemovingRoute)}/outboundconnections`; + return new BlockchainDataManagerNetworkNode( + output.name, + '*', + subscriptionId, + resourceGroup, + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT, + url); + }); + + const outputs = new BlockchainDataManagerInputAndOutput( + ItemType.BLOCKCHAIN_DATA_MANAGER_OUTPUT_GROUP, + Constants.treeItemData.group.bdm.output.label, + ); + + outputs.setChildren(bdmOutputs); + + return outputs; + } + + private getInputUrl(url: string): string { + const firstMark = 'blockchainMembers/'; + const defaultInputTransactionNode = 'transaction-node'; + const nodeName = 'nodeName'; + + const idxOfMemberAndTransactionNode = url.lastIndexOf(firstMark) + firstMark.length; + const relativeInternalPath = url.substring(0, idxOfMemberAndTransactionNode).split('/').join('%2F'); + let memberAndTransactionNode = url.substring(idxOfMemberAndTransactionNode) + .replace('transactionNodes', nodeName); + + if (memberAndTransactionNode.includes(defaultInputTransactionNode)) { + const memberName = memberAndTransactionNode.substring(0, memberAndTransactionNode.indexOf('/' + nodeName)); + memberAndTransactionNode = memberAndTransactionNode.replace(defaultInputTransactionNode, memberName); + } + + return `${Constants.azureResourceExplorer.portalBladeUri}/overview/memberResourceId/` + + relativeInternalPath + memberAndTransactionNode; + } + + private async getAzureClient(subscriptionItem: SubscriptionItem, resourceGroupItem: ResourceGroupItem) + : Promise { + return new AzureBlockchainServiceClient( + subscriptionItem.session.credentials, + subscriptionItem.subscriptionId, + resourceGroupItem.label, + resourceGroupItem.description, + Constants.azureResourceExplorer.requestBaseUri, + { + acceptLanguage: Constants.azureResourceExplorer.requestAcceptLanguage, + filters: [], + generateClientRequestId: true, + longRunningOperationRetryTimeout: 30, + noRetryPolicy: false, + requestOptions: { + customHeaders: {}, + }, + rpRegistrationRetryTimeout: 30, + }, + ); + } +} diff --git a/src/resourceExplorers/ConsortiumResourceExplorer.ts b/src/resourceExplorers/ConsortiumResourceExplorer.ts index b6b6bd3..3447d5f 100644 --- a/src/resourceExplorers/ConsortiumResourceExplorer.ts +++ b/src/resourceExplorers/ConsortiumResourceExplorer.ts @@ -87,7 +87,6 @@ export class ConsortiumResourceExplorer extends AzureResourceExplorer { resourceGroupItem.label, resourceGroupItem.description, Constants.azureResourceExplorer.requestBaseUri, - Constants.azureResourceExplorer.requestApiVersion, { acceptLanguage: Constants.azureResourceExplorer.requestAcceptLanguage, filters: [], diff --git a/src/resourceExplorers/index.ts b/src/resourceExplorers/index.ts index d5fc7b2..354e138 100644 --- a/src/resourceExplorers/index.ts +++ b/src/resourceExplorers/index.ts @@ -2,6 +2,7 @@ // Licensed under the MIT license. export * from './AzureResourceExplorer'; +export * from './BlockchainDataManagerResourceExplorer'; export * from './ConsortiumResourceExplorer'; export * from './InfuraResourceExplorer'; export * from './LocalResourceExplorer'; diff --git a/src/services/contract/ContractService.ts b/src/services/contract/ContractService.ts index ac5c9d6..11505d0 100644 --- a/src/services/contract/ContractService.ts +++ b/src/services/contract/ContractService.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import { HttpService } from '..'; import { Constants } from '../../Constants'; import { getWorkspaceRoot, TruffleConfiguration } from '../../helpers'; +import { Telemetry } from '../../TelemetryClient'; import { Contract } from './Contract'; export namespace ContractService { @@ -68,7 +69,7 @@ export namespace ContractService { const buildDir = await getPathDirectory('contracts_build_directory'); if (!fs.pathExistsSync(buildDir)) { - throw new Error(Constants.errorMessageStrings.BuildContractsDirIsNotExist(buildDir)); + throw new Error(Constants.errorMessageStrings.BuildContractsDirDoesNotExist(Telemetry.obfuscate(buildDir))); } return fs.readdirSync(buildDir) diff --git a/src/services/index.ts b/src/services/index.ts index c9bda15..a751ff4 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -15,3 +15,4 @@ export * from './tree/TreeService'; export * from './openZeppelin/OpenZeppelinService'; export * from './openZeppelin/OpenZeppelinMigrationsService'; export * from './infuraService/InfuraServiceClient'; +export * from './openZeppelin/OpenZeppelinProjectJsonService'; diff --git a/src/services/openZeppelin/OpenZeppelinMigrationsService.ts b/src/services/openZeppelin/OpenZeppelinMigrationsService.ts index 85799b6..d99dbf3 100644 --- a/src/services/openZeppelin/OpenZeppelinMigrationsService.ts +++ b/src/services/openZeppelin/OpenZeppelinMigrationsService.ts @@ -54,7 +54,10 @@ function generateContractDeployerSection(items: IOZAsset[]): string { const contractNames = OpenZeppelinService.getContractsNamesFromAsset(asset); return deployerSections - .concat(contractNames.map((contract) => ` deployer.deploy(${contract});`)); + .concat(contractNames.map((contract) => { + const contractParameters = OpenZeppelinService.getContractParametersFromAsset(asset, contract); + return ` deployer.deploy(${[contract, ...contractParameters].join(', ')});`; + })); }, []) .join('\n'); } diff --git a/src/services/openZeppelin/OpenZeppelinProjectJsonService.ts b/src/services/openZeppelin/OpenZeppelinProjectJsonService.ts index 89eaab4..46e8652 100644 --- a/src/services/openZeppelin/OpenZeppelinProjectJsonService.ts +++ b/src/services/openZeppelin/OpenZeppelinProjectJsonService.ts @@ -62,28 +62,19 @@ export namespace OpenZeppelinProjectJsonService { export async function addAssetsToProjectJsonAsync( downloadedAssets: IOZAsset[], - makePersist: boolean = true, - updatingProjectMetadata?: IProjectMetadata, - overWrite: boolean = false) + projectMetadata: IProjectMetadata) : Promise { - const projectMetadata = updatingProjectMetadata || await getProjectJson(); const newStored: IOZAsset[] = []; newStored.push(...downloadedAssets); - if (!overWrite) { - projectMetadata.openZeppelin.assets.forEach((storedAsset) => { - if (!downloadedAssets.some((downloaded) => downloaded.id === storedAsset.id)) { - newStored.push(storedAsset); - } - }); - } + projectMetadata.openZeppelin.assets.forEach((storedAsset) => { + if (!downloadedAssets.some((downloaded) => downloaded.id === storedAsset.id)) { + newStored.push(storedAsset); + } + }); projectMetadata.openZeppelin.assets = newStored; - if (makePersist) { - await storeProjectJsonAsync(projectMetadata); - } - return projectMetadata; } diff --git a/src/services/openZeppelin/OpenZeppelinService.ts b/src/services/openZeppelin/OpenZeppelinService.ts index 9bcf2ee..376c7a6 100644 --- a/src/services/openZeppelin/OpenZeppelinService.ts +++ b/src/services/openZeppelin/OpenZeppelinService.ts @@ -119,7 +119,7 @@ export namespace OpenZeppelinService { updatedProjectJson = await OpenZeppelinProjectJsonService .addCategoryToProjectJsonAsync(category, false, updatedProjectJson); updatedProjectJson = await OpenZeppelinProjectJsonService - .addAssetsToProjectJsonAsync(assets, false, updatedProjectJson); + .addAssetsToProjectJsonAsync(assets, updatedProjectJson); return OpenZeppelinProjectJsonService.storeProjectJsonAsync(updatedProjectJson); } @@ -139,6 +139,30 @@ export namespace OpenZeppelinService { return []; } + export function getContractParametersFromAsset(asset: IOZAsset, contractName: string): string[] { + if (asset.requiredParameters) { + const parameters = asset.requiredParameters[contractName]; + + if (parameters) { + return parameters.reduce((arr: string[], param) => { + if (param.value) { + if (param.type === 'string') { + arr.push(`"${param.value}"`); + } else if (param.type.match(Constants.validationRegexps.types.simpleArray)) { + arr.push(getArrayParameterValue(param.value, param.type)); + } else { + arr.push(param.value); + } + } + + return arr; + }, []); + } + } + + return []; + } + export function getContractNameByAssetName(asset: IOZAsset): string { return path.parse(asset.name).name; } @@ -205,7 +229,8 @@ export namespace OpenZeppelinService { } try { - await createNewProjectJsonAsync(newManifest.getVersion(), newAssets, tempNewOzFolder); + const mergedAssets = await mergeAssetsWithExisting(newAssets); + await createNewProjectJsonAsync(newManifest.getVersion(), mergedAssets, tempNewOzFolder); } catch { throwOpenZeppelinUpgradeException(userTmpFolderPath); } @@ -225,6 +250,38 @@ export namespace OpenZeppelinService { Telemetry.sendEvent('OpenZeppelinService.updateOpenZeppelinContractsAsync.finished'); } + + export async function getAssetsWithParameters(): Promise { + const currentAssets = await getAllDownloadedAssetsAsync(); + + return currentAssets.filter((asset) => !!asset.requiredParameters); + } + + export async function mergeAssetsWithExisting(currentAssets: IOZAsset[]): Promise { + const assetsWithParameters = await OpenZeppelinService.getAssetsWithParameters(); + + return currentAssets.map((asset) => { + if (asset.requiredParameters) { + const assetWithParameters = assetsWithParameters.find((element) => element.id === asset.id); + if (assetWithParameters && assetWithParameters.requiredParameters) { + for (const [currentContractName, currentContractParameters] of Object.entries(asset.requiredParameters)) { + const existedContractParameters = assetWithParameters.requiredParameters[currentContractName]; + if (existedContractParameters) { + for (const parameter of currentContractParameters) { + const param = existedContractParameters.find((existedContractParameter) => + existedContractParameter.name === parameter.name && + existedContractParameter.type === parameter.type); + if (param) { + parameter.value = param.value; + } + } + } + } + } + } + return asset; + }); + } } function getOzContractsFromProjectMetadata( @@ -285,7 +342,7 @@ async function moveProjectJsonAsync(nonUserProjectFolder: string, folderIsDestin async function createNewProjectJsonAsync(version: string, assets: IOZAsset[], folder: string): Promise { let projectJson = OpenZeppelinProjectJsonService.getProjectJson(); projectJson = await OpenZeppelinProjectJsonService.addVersionToProjectJsonAsync(version, false, projectJson); - projectJson = await OpenZeppelinProjectJsonService.addAssetsToProjectJsonAsync(assets, false, projectJson, true); + projectJson = await OpenZeppelinProjectJsonService.addAssetsToProjectJsonAsync(assets, projectJson); const newPath = path.join(folder, OpenZeppelinProjectJsonService.getProjectJsonFileName()); return OpenZeppelinProjectJsonService.storeProjectJsonAsync(projectJson, newPath); @@ -325,3 +382,13 @@ async function downloadNewVersionOfAssetsAsync( return { isDownloadSucceed: true, newAssets }; } + +function getArrayParameterValue(value: string, type: string): string { + if (type !== 'string[]') { + return value; + } + + const elements = value.slice(1, value.length - 1).split(','); + const values = elements?.map((element) => `"${element.trim()}"`); + return `[${values?.join(', ')}]`; +} diff --git a/src/services/openZeppelin/manifest-2.3.0.json b/src/services/openZeppelin/manifest-2.3.0.json index 2c40749..ae1f752 100644 --- a/src/services/openZeppelin/manifest-2.3.0.json +++ b/src/services/openZeppelin/manifest-2.3.0.json @@ -3,7 +3,6 @@ "baseUri": "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/", "targetPoint": "5fe8f4e93bd1d4f5cc9a6899d7f24f5ffe4c14aa/contracts/", "apiDocumentationBaseUri": "https://docs.openzeppelin.com/contracts/2.x/api", - "openZeppelinVersion": "2.3.0", "categories": [ { "name": "Access Control", @@ -210,24 +209,6 @@ } ], "assets": [ - { - "id": "ownable", - "name": "ownership/Ownable.sol", - "hash": "9c7b5dedc4e71ee1c6291c4add8d3b37f6d5f880", - "type": "abstractContract" - }, - { - "id": "address", - "name": "utils/Address.sol", - "hash": "4a07b92da5c63dc1aa7e6d8ab0538085767fcfe5", - "type": "library" - }, - { - "id": "math", - "name": "math/Math.sol", - "hash": "4970a6cc629796229b07088d5b9cca2b9a24c9a5", - "type": "library" - }, { "id": "roles", "name": "access/Roles.sol", @@ -235,16 +216,59 @@ "type": "library" }, { - "id": "ierc165", - "name": "introspection/IERC165.sol", - "hash": "5702b588eb9b7fae49c9129f02a9de7fb19b1293", - "type": "interface" + "id": "capperrole", + "name": "access/roles/CapperRole.sol", + "hash": "7c1af8a8c73ff27325ba7b33046fde4d2fb3480c", + "type": "abstractContract", + "dependencies": [ + "roles" + ] }, { - "id": "ecdsa", - "name": "cryptography/ECDSA.sol", - "hash": "630362127a8844441a956b269279a6de253b5bcb", - "type": "library" + "id": "minterrole", + "name": "access/roles/MinterRole.sol", + "hash": "a84367cf3022b9a1686257b46c2f0f0c8daaad44", + "type": "abstractContract", + "dependencies": [ + "roles" + ] + }, + { + "id": "pauserrole", + "name": "access/roles/PauserRole.sol", + "hash": "f149139aad5f91e7f215733967c1ae3d58da9f09", + "type": "abstractContract", + "dependencies": [ + "roles" + ] + }, + { + "id": "signerrole", + "name": "access/roles/SignerRole.sol", + "hash": "468d0aef1850b0541efe2ad87b80c5a7ec35416c", + "type": "abstractContract", + "dependencies": [ + "roles" + ] + }, + { + "id": "whitelistadminrole", + "name": "access/roles/WhitelistAdminRole.sol", + "hash": "8c16928feb70b35ca8b2c72dae2fb330a7ca52b2", + "type": "abstractContract", + "dependencies": [ + "roles" + ] + }, + { + "id": "whitelistedrole", + "name": "access/roles/WhitelistedRole.sol", + "hash": "326d3343ac605da3179f81074b27b308238170a5", + "type": "contract", + "dependencies": [ + "roles", + "whitelistadminrole" + ] }, { "id": "ierc20", @@ -259,59 +283,11 @@ "type": "library" }, { - "id": "merkleproof", - "name": "cryptography/MerkleProof.sol", - "hash": "83a861546e70529ea2dded6e7061c0d0f6f5474c", + "id": "address", + "name": "utils/Address.sol", + "hash": "4a07b92da5c63dc1aa7e6d8ab0538085767fcfe5", "type": "library" }, - { - "id": "erc165", - "name": "introspection/ERC165.sol", - "hash": "adea7391f6e0f043e8b15bb5100f5cb43cfa6ab9", - "type": "abstractContract", - "dependencies": [ - "ierc165" - ] - }, - { - "id": "pauserrole", - "name": "access/roles/PauserRole.sol", - "hash": "f149139aad5f91e7f215733967c1ae3d58da9f09", - "type": "abstractContract", - "dependencies": [ - "roles" - ] - }, - { - "id": "addressimpl", - "name": "mocks/AddressImpl.sol", - "hash": "a73591c370a96c61d55e74d371af47d793eac388", - "type": "contract", - "dependencies": [ - "address" - ] - }, - { - "id": "secondary", - "name": "ownership/Secondary.sol", - "hash": "b34be41fda523bbc86142ba2c5398c4753e2c699", - "type": "abstractContract" - }, - { - "id": "ierc777", - "name": "token/ERC777/IERC777.sol", - "hash": "776de1dc9ba0e66afa949e98b07ae564445ee973", - "type": "interface" - }, - { - "id": "capperrole", - "name": "access/roles/CapperRole.sol", - "hash": "7c1af8a8c73ff27325ba7b33046fde4d2fb3480c", - "type": "abstractContract", - "dependencies": [ - "roles" - ] - }, { "id": "safeerc20", "name": "token/ERC20/SafeERC20.sol", @@ -323,55 +299,6 @@ "address" ] }, - { - "id": "pausable", - "name": "lifecycle/Pausable.sol", - "hash": "ab74d47fdadc5e9c69b1a4f687ea5b0bd7ac9013", - "type": "abstractContract", - "dependencies": [ - "pauserrole" - ] - }, - { - "id": "erc165checker", - "name": "introspection/ERC165Checker.sol", - "hash": "8653da53ab05d6049fb71b64afe39ed8d9537203", - "type": "library" - }, - { - "id": "erc20", - "name": "token/ERC20/ERC20.sol", - "hash": "23cbcb84547da7772a7afe432ce9cf1bbc430515", - "type": "contract", - "dependencies": [ - "ierc20", - "safemath" - ] - }, - { - "id": "ierc721", - "name": "token/ERC721/IERC721.sol", - "hash": "960c1f902529069ee5352fdbb9d75a54b42eaf9c", - "type": "contract", - "dependencies": [ - "ierc165" - ] - }, - { - "id": "ierc777recipient", - "name": "token/ERC777/IERC777Recipient.sol", - "hash": "a46b30a9f659ff492e3c169b3ef49bc87c5cfea3", - "type": "interface" - }, - { - "id": "arrays", - "name": "utils/Arrays.sol", - "hash": "cbc59a1e2bd457b09d00ced5b9ed7ad38c32b478", - "type": "library", - "dependencies": [ - "math" - ] - }, { "id": "reentrancyguard", "name": "utils/ReentrancyGuard.sol", @@ -379,19 +306,85 @@ "type": "abstractContract" }, { - "id": "minterrole", - "name": "access/roles/MinterRole.sol", - "hash": "a84367cf3022b9a1686257b46c2f0f0c8daaad44", + "id": "crowdsale", + "name": "crowdsale/Crowdsale.sol", + "hash": "824ba83cf3098c5c4c0bc008830133f7c2a2aaf2", + "type": "contract", + "dependencies": [ + "ierc20", + "safemath", + "safeerc20", + "reentrancyguard" + ], + "requiredParameters": { + "Crowdsale": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } + }, + { + "id": "timedcrowdsale", + "name": "crowdsale/validation/TimedCrowdsale.sol", + "hash": "255a63f3485a496858d761c869433eea12d172b6", "type": "abstractContract", "dependencies": [ - "roles" + "safemath", + "crowdsale" + ], + "requiredParameters": { + "TimedCrowdsale": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + } + ] + } + }, + { + "id": "finalizablecrowdsale", + "name": "crowdsale/distribution/FinalizableCrowdsale.sol", + "hash": "9c42e6146c1ab1a3c0cedd526c19093af7c88c5d", + "type": "abstractContract", + "dependencies": [ + "safemath", + "timedcrowdsale" ] }, { - "id": "ierc1820implementer", - "name": "introspection/IERC1820Implementer.sol", - "hash": "965676faaa56683a81dfa09fd646417a44c27147", - "type": "interface" + "id": "secondary", + "name": "ownership/Secondary.sol", + "hash": "b34be41fda523bbc86142ba2c5398c4753e2c699", + "type": "abstractContract" + }, + { + "id": "postdeliverycrowdsale", + "name": "crowdsale/distribution/PostDeliveryCrowdsale.sol", + "hash": "41b9059b7200201e1b75c7d0c384dd9798071f2e", + "contracts": [ + "PostDeliveryCrowdsale", + "__unstable__TokenVault" + ], + "dependencies": [ + "timedcrowdsale", + "safemath", + "secondary", + "ierc20" + ] }, { "id": "escrow", @@ -404,77 +397,95 @@ ] }, { - "id": "erc20burnable", - "name": "token/ERC20/ERC20Burnable.sol", - "hash": "de2234da510aa535ef6cc566d3fe3dc1e280b267", - "type": "contract", - "dependencies": [ - "erc20" - ] - }, - { - "id": "crowdsale", - "name": "crowdsale/Crowdsale.sol", - "hash": "824ba83cf3098c5c4c0bc008830133f7c2a2aaf2", - "type": "contract", - "dependencies": [ - "ierc20", - "safemath", - "safeerc20", - "reentrancyguard" - ] - }, - { - "id": "ierc777sender", - "name": "token/ERC777/IERC777Sender.sol", - "hash": "6a158c52d2e41e9b23f31dc3f979e1d94aed50d5", - "type": "interface" - }, - { - "id": "ierc721receiver", - "name": "token/ERC721/IERC721Receiver.sol", - "hash": "b0618c0ec70781add71d928c8c0b633630a6d7a9", - "type": "contract" - }, - { - "id": "erc1820implementer", - "name": "introspection/ERC1820Implementer.sol", - "hash": "6132022b47beed18f7068c0c4ce340dcf272d4d7", - "type": "contract", - "dependencies": [ - "ierc1820implementer" - ] - }, - { - "id": "signerrole", - "name": "access/roles/SignerRole.sol", - "hash": "468d0aef1850b0541efe2ad87b80c5a7ec35416c", + "id": "conditionalescrow", + "name": "payment/escrow/ConditionalEscrow.sol", + "hash": "0cbe8f044dda32e90d31cebec7a40f00e644d6a3", "type": "abstractContract", "dependencies": [ - "roles" + "escrow" ] }, + { + "id": "refundescrow", + "name": "payment/escrow/RefundEscrow.sol", + "hash": "18d38f6bcf59a796ff4681ff1c3c086099038c3d", + "type": "contract", + "dependencies": [ + "conditionalescrow" + ], + "requiredParameters": { + "RefundEscrow": [ + { + "name": "beneficiary", + "type": "address" + } + ] + } + }, + { + "id": "refundablecrowdsale", + "name": "crowdsale/distribution/RefundableCrowdsale.sol", + "hash": "d910441312b505f4df921d58005fb2b18c592913", + "type": "abstractContract", + "dependencies": [ + "safemath", + "finalizablecrowdsale", + "refundescrow" + ], + "requiredParameters": { + "RefundableCrowdsale": [ + { + "name": "goal", + "type": "uint256" + } + ] + } + }, + { + "id": "refundablepostdeliverycrowdsale", + "name": "crowdsale/distribution/RefundablePostDeliveryCrowdsale.sol", + "hash": "385aa195ca0ff57ffa731e24e9cb48e8bf5cef88", + "type": "abstractContract", + "dependencies": [ + "refundablecrowdsale", + "postdeliverycrowdsale" + ] + }, + { + "id": "math", + "name": "math/Math.sol", + "hash": "4970a6cc629796229b07088d5b9cca2b9a24c9a5", + "type": "library" + }, { "id": "allowancecrowdsale", "name": "crowdsale/emission/AllowanceCrowdsale.sol", "hash": "beee692a6c432fc35919f8949214967ece5a52a9", - "type": "contract", + "type": "abstractContract", "dependencies": [ "crowdsale", "ierc20", "safeerc20", "safemath", "math" - ] + ], + "requiredParameters": { + "AllowanceCrowdsale": [ + { + "name": "tokenWallet", + "type": "address" + } + ] + } }, { - "id": "timedcrowdsale", - "name": "crowdsale/validation/TimedCrowdsale.sol", - "hash": "255a63f3485a496858d761c869433eea12d172b6", + "id": "erc20", + "name": "token/ERC20/ERC20.sol", + "hash": "23cbcb84547da7772a7afe432ce9cf1bbc430515", "type": "contract", "dependencies": [ - "safemath", - "crowdsale" + "ierc20", + "safemath" ] }, { @@ -488,12 +499,141 @@ ] }, { - "id": "conditionalescrow", - "name": "payment/escrow/ConditionalEscrow.sol", - "hash": "0cbe8f044dda32e90d31cebec7a40f00e644d6a3", + "id": "mintedcrowdsale", + "name": "crowdsale/emission/MintedCrowdsale.sol", + "hash": "7815b8cacec298d6da1f7b3bffe5d3b01b949c73", + "type": "abstractContract", + "dependencies": [ + "crowdsale", + "erc20mintable" + ] + }, + { + "id": "increasingpricecrowdsale", + "name": "crowdsale/price/IncreasingPriceCrowdsale.sol", + "hash": "964514ba204ad0dd55c989e75b9d3b9ccc5d5122", + "type": "abstractContract", + "dependencies": [ + "timedcrowdsale", + "safemath" + ], + "requiredParameters": { + "IncreasingPriceCrowdsale": [ + { + "name": "initialRate", + "type": "uint256" + }, + { + "name": "finalRate", + "type": "uint256" + } + ] + } + }, + { + "id": "cappedcrowdsale", + "name": "crowdsale/validation/CappedCrowdsale.sol", + "hash": "e8aeede14936773b42db448c6504349c10063d0a", + "type": "abstractContract", + "dependencies": [ + "safemath", + "crowdsale" + ], + "requiredParameters": { + "CappedCrowdsale": [ + { + "name": "cap", + "type": "uint256" + } + ] + } + }, + { + "id": "individuallycappedcrowdsale", + "name": "crowdsale/validation/IndividuallyCappedCrowdsale.sol", + "hash": "1f9df361df0c8ee8aec7e981f787ec0c9c877d10", + "type": "abstractContract", + "dependencies": [ + "safemath", + "crowdsale", + "capperrole" + ] + }, + { + "id": "pausable", + "name": "lifecycle/Pausable.sol", + "hash": "ab74d47fdadc5e9c69b1a4f687ea5b0bd7ac9013", + "type": "abstractContract", + "dependencies": [ + "pauserrole" + ] + }, + { + "id": "pausablecrowdsale", + "name": "crowdsale/validation/PausableCrowdsale.sol", + "hash": "cc89aebab461fa75cb6f36523b655bcbd297a8fd", + "type": "abstractContract", + "dependencies": [ + "crowdsale", + "pausable" + ] + }, + { + "id": "whitelistcrowdsale", + "name": "crowdsale/validation/WhitelistCrowdsale.sol", + "hash": "0f5965352e88d4e03e3c576bd814952762de8fc2", + "type": "abstractContract", + "dependencies": [ + "crowdsale", + "whitelistedrole" + ] + }, + { + "id": "ecdsa", + "name": "cryptography/ECDSA.sol", + "hash": "630362127a8844441a956b269279a6de253b5bcb", + "type": "library" + }, + { + "id": "merkleproof", + "name": "cryptography/MerkleProof.sol", + "hash": "83a861546e70529ea2dded6e7061c0d0f6f5474c", + "type": "library" + }, + { + "id": "ierc165", + "name": "introspection/IERC165.sol", + "hash": "5702b588eb9b7fae49c9129f02a9de7fb19b1293", + "type": "interface" + }, + { + "id": "erc165", + "name": "introspection/ERC165.sol", + "hash": "adea7391f6e0f043e8b15bb5100f5cb43cfa6ab9", + "type": "abstractContract", + "dependencies": [ + "ierc165" + ] + }, + { + "id": "erc165checker", + "name": "introspection/ERC165Checker.sol", + "hash": "8653da53ab05d6049fb71b64afe39ed8d9537203", + "type": "library" + }, + { + "id": "ierc1820implementer", + "name": "introspection/IERC1820Implementer.sol", + "hash": "965676faaa56683a81dfa09fd646417a44c27147", + "type": "interface" + }, + { + "id": "erc1820implementer", + "name": "introspection/ERC1820Implementer.sol", + "hash": "6132022b47beed18f7068c0c4ce340dcf272d4d7", "type": "contract", "dependencies": [ - "escrow" + "ierc1820implementer" ] }, { @@ -503,13 +643,12 @@ "type": "interface" }, { - "id": "finalizablecrowdsale", - "name": "crowdsale/distribution/FinalizableCrowdsale.sol", - "hash": "9c42e6146c1ab1a3c0cedd526c19093af7c88c5d", - "type": "abstractContract", + "id": "addressimpl", + "name": "mocks/AddressImpl.sol", + "hash": "a73591c370a96c61d55e74d371af47d793eac388", + "type": "contract", "dependencies": [ - "safemath", - "timedcrowdsale" + "address" ] }, { @@ -520,33 +659,35 @@ "dependencies": [ "ierc20", "allowancecrowdsale" - ] + ], + "requiredParameters": { + "AllowanceCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "tokenWallet", + "type": "address" + } + ] + } }, { - "id": "whitelistadminrole", - "name": "access/roles/WhitelistAdminRole.sol", - "hash": "8c16928feb70b35ca8b2c72dae2fb330a7ca52b2", - "type": "abstractContract", - "dependencies": [ - "roles" - ] - }, - { - "id": "counters", - "name": "drafts/Counters.sol", - "hash": "2d4db92b89798e85ae176b7b2c8bf4743cdfaf34", + "id": "arrays", + "name": "utils/Arrays.sol", + "hash": "cbc59a1e2bd457b09d00ced5b9ed7ad38c32b478", "type": "library", "dependencies": [ - "safemath" - ] - }, - { - "id": "erc20capped", - "name": "token/ERC20/ERC20Capped.sol", - "hash": "36bc9a9b0c708bfae620011940b3263f2e700ca6", - "type": "contract", - "dependencies": [ - "erc20mintable" + "math" ] }, { @@ -556,86 +697,15 @@ "type": "contract", "dependencies": [ "arrays" - ] - }, - { - "id": "postdeliverycrowdsale", - "name": "crowdsale/distribution/PostDeliveryCrowdsale.sol", - "hash": "41b9059b7200201e1b75c7d0c384dd9798071f2e", - "type": "contract", - "dependencies": [ - "timedcrowdsale", - "safemath", - "secondary", - "ierc20" - ] - }, - { - "id": "erc777", - "name": "token/ERC777/ERC777.sol", - "hash": "f857512c83bf6245f4a25db7dc2e2f16c1017f2b", - "type": "contract", - "dependencies": [ - "ierc777", - "ierc777recipient", - "ierc777sender", - "ierc20", - "safemath", - "address", - "ierc1820registry" - ] - }, - { - "id": "refundescrow", - "name": "payment/escrow/RefundEscrow.sol", - "hash": "18d38f6bcf59a796ff4681ff1c3c086099038c3d", - "type": "contract", - "dependencies": [ - "conditionalescrow" - ] - }, - { - "id": "whitelistedrole", - "name": "access/roles/WhitelistedRole.sol", - "hash": "326d3343ac605da3179f81074b27b308238170a5", - "type": "contract", - "dependencies": [ - "roles", - "whitelistadminrole" - ] - }, - { - "id": "cappedcrowdsale", - "name": "crowdsale/validation/CappedCrowdsale.sol", - "hash": "e8aeede14936773b42db448c6504349c10063d0a", - "type": "contract", - "dependencies": [ - "safemath", - "crowdsale" - ] - }, - { - "id": "erc20detailed", - "name": "token/ERC20/ERC20Detailed.sol", - "hash": "baa9ba5c46a405234eb7159f18455f71f8127c29", - "type": "contract", - "dependencies": [ - "ierc20" - ] - }, - { - "id": "erc721", - "name": "token/ERC721/ERC721.sol", - "hash": "1c9a79de3eee0772a59b57d0ff865fe10b1094f6", - "type": "contract", - "dependencies": [ - "ierc721", - "ierc721receiver", - "safemath", - "address", - "counters", - "erc165" - ] + ], + "requiredParameters": { + "ArraysImpl": [ + { + "name": "_array", + "type": "uint256[]" + } + ] + } }, { "id": "cappedcrowdsaleimpl", @@ -645,46 +715,27 @@ "dependencies": [ "ierc20", "cappedcrowdsale" - ] - }, - { - "id": "refundablecrowdsale", - "name": "crowdsale/distribution/RefundableCrowdsale.sol", - "hash": "d910441312b505f4df921d58005fb2b18c592913", - "type": "contract", - "dependencies": [ - "safemath", - "finalizablecrowdsale", - "refundescrow" - ] - }, - { - "id": "paymentsplitter", - "name": "payment/PaymentSplitter.sol", - "hash": "7064404485ace9a427a0293ee11087aa621293e0", - "type": "contract", - "dependencies": [ - "safemath" - ] - }, - { - "id": "erc721burnable", - "name": "token/ERC721/ERC721Burnable.sol", - "hash": "562a17e8e409981c795a5b84798a3b63cb24f465", - "type": "contract", - "dependencies": [ - "erc721" - ] - }, - { - "id": "erc20pausable", - "name": "token/ERC20/ERC20Pausable.sol", - "hash": "3304cc033cb225a23e5e38b058f540af845d703a", - "type": "contract", - "dependencies": [ - "erc20", - "pausable" - ] + ], + "requiredParameters": { + "CappedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "cap", + "type": "uint256" + } + ] + } }, { "id": "capperrolemock", @@ -695,43 +746,6 @@ "capperrole" ] }, - { - "id": "pullpayment", - "name": "payment/PullPayment.sol", - "hash": "9e326514ba11403b1f485ecb771fd3eba5a66f0a", - "type": "abstractContract", - "dependencies": [ - "escrow" - ] - }, - { - "id": "refundablepostdeliverycrowdsale", - "name": "crowdsale/distribution/RefundablePostDeliveryCrowdsale.sol", - "hash": "385aa195ca0ff57ffa731e24e9cb48e8bf5cef88", - "type": "contract", - "dependencies": [ - "refundablecrowdsale", - "postdeliverycrowdsale" - ] - }, - { - "id": "tokentimelock", - "name": "token/ERC20/TokenTimelock.sol", - "hash": "23dc2f9c3b4ac524a7402b77c8cc3eb8f1e16989", - "type": "contract", - "dependencies": [ - "safeerc20" - ] - }, - { - "id": "ierc721enumerable", - "name": "token/ERC721/IERC721Enumerable.sol", - "hash": "4c1dfb564a11820ec63b890aaf45a1ab349bd72e", - "type": "contract", - "dependencies": [ - "ierc721" - ] - }, { "id": "conditionalescrowmock", "name": "mocks/ConditionalEscrowMock.sol", @@ -742,24 +756,12 @@ ] }, { - "id": "mintedcrowdsale", - "name": "crowdsale/emission/MintedCrowdsale.sol", - "hash": "7815b8cacec298d6da1f7b3bffe5d3b01b949c73", - "type": "contract", + "id": "counters", + "name": "drafts/Counters.sol", + "hash": "2d4db92b89798e85ae176b7b2c8bf4743cdfaf34", + "type": "library", "dependencies": [ - "crowdsale", - "erc20mintable" - ] - }, - { - "id": "erc721enumerable", - "name": "token/ERC721/ERC721Enumerable.sol", - "hash": "ed987e3ab3f188e9af282e0df30677ddc2eb9e87", - "type": "contract", - "dependencies": [ - "ierc721enumerable", - "erc721", - "erc165" + "safemath" ] }, { @@ -771,25 +773,6 @@ "counters" ] }, - { - "id": "increasingpricecrowdsale", - "name": "crowdsale/price/IncreasingPriceCrowdsale.sol", - "hash": "964514ba204ad0dd55c989e75b9d3b9ccc5d5122", - "type": "contract", - "dependencies": [ - "timedcrowdsale", - "safemath" - ] - }, - { - "id": "ierc721metadata", - "name": "token/ERC721/IERC721Metadata.sol", - "hash": "05835aff87f27c8b3195eac2b9d06d3ed0c5489e", - "type": "contract", - "dependencies": [ - "ierc721" - ] - }, { "id": "crowdsalemock", "name": "mocks/CrowdsaleMock.sol", @@ -797,29 +780,23 @@ "type": "contract", "dependencies": [ "crowdsale" - ] - }, - { - "id": "individuallycappedcrowdsale", - "name": "crowdsale/validation/IndividuallyCappedCrowdsale.sol", - "hash": "1f9df361df0c8ee8aec7e981f787ec0c9c877d10", - "type": "contract", - "dependencies": [ - "safemath", - "crowdsale", - "capperrole" - ] - }, - { - "id": "erc721metadata", - "name": "token/ERC721/ERC721Metadata.sol", - "hash": "ce263ddc7f40803dfe70cf13eeb324ccd83860bd", - "type": "contract", - "dependencies": [ - "erc721", - "ierc721metadata", - "erc165" - ] + ], + "requiredParameters": { + "CrowdsaleMock": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "ecdsamock", @@ -830,54 +807,25 @@ "ecdsa" ] }, - { - "id": "pausablecrowdsale", - "name": "crowdsale/validation/PausableCrowdsale.sol", - "hash": "cc89aebab461fa75cb6f36523b655bcbd297a8fd", - "type": "contract", - "dependencies": [ - "crowdsale", - "pausable" - ] - }, - { - "id": "erc721full", - "name": "token/ERC721/ERC721Full.sol", - "hash": "308ea5265964963799ef3d40042d8fa3e5261c87", - "type": "contract", - "dependencies": [ - "erc721", - "erc721enumerable", - "erc721metadata" - ] - }, { "id": "erc165interfacessupported", "name": "mocks/ERC165/ERC165InterfacesSupported.sol", "hash": "ab4d5a5dfcbbf0b65f310565ec6a01f90a78d170", - "type": "contract", + "contracts": [ + "SupportsInterfaceWithLookupMock", + "ERC165InterfacesSupported" + ], "dependencies": [ "ierc165" - ] - }, - { - "id": "whitelistcrowdsale", - "name": "crowdsale/validation/WhitelistCrowdsale.sol", - "hash": "0f5965352e88d4e03e3c576bd814952762de8fc2", - "type": "contract", - "dependencies": [ - "crowdsale", - "whitelistedrole" - ] - }, - { - "id": "erc721holder", - "name": "token/ERC721/ERC721Holder.sol", - "hash": "bcc10d7e225495d51f897f31254cc128dde58669", - "type": "contract", - "dependencies": [ - "ierc721receiver" - ] + ], + "requiredParameters": { + "ERC165InterfacesSupported": [ + { + "name": "interfaceIds", + "type": "bytes4[]" + } + ] + } }, { "id": "erc165notsupported", @@ -885,16 +833,6 @@ "hash": "d154da33ea2700a44abd0f4cea6a2aec67845eeb", "type": "contract" }, - { - "id": "erc721metadatamintable", - "name": "token/ERC721/ERC721MetadataMintable.sol", - "hash": "a1421d1dd64d3c2b5546e5f710350fc708439af6", - "type": "contract", - "dependencies": [ - "erc721metadata", - "minterrole" - ] - }, { "id": "erc165checkermock", "name": "mocks/ERC165CheckerMock.sol", @@ -904,16 +842,6 @@ "erc165checker" ] }, - { - "id": "erc721mintable", - "name": "token/ERC721/ERC721Mintable.sol", - "hash": "98cd7a3a8ddf9898fe8e9c5800063ecf27b46114", - "type": "contract", - "dependencies": [ - "erc721", - "minterrole" - ] - }, { "id": "erc165mock", "name": "mocks/ERC165Mock.sol", @@ -923,16 +851,6 @@ "erc165" ] }, - { - "id": "erc721pausable", - "name": "token/ERC721/ERC721Pausable.sol", - "hash": "7cfd39ae6cd32c6f48c20654cacae74158b8ef0e", - "type": "contract", - "dependencies": [ - "erc721", - "pausable" - ] - }, { "id": "erc1820implementermock", "name": "mocks/ERC1820ImplementerMock.sol", @@ -943,14 +861,12 @@ ] }, { - "id": "ierc721full", - "name": "token/ERC721/IERC721Full.sol", - "hash": "e543aefeb38ae89d3cad665e81a89ab9d4d59dac", + "id": "erc20burnable", + "name": "token/ERC20/ERC20Burnable.sol", + "hash": "de2234da510aa535ef6cc566d3fe3dc1e280b267", "type": "contract", "dependencies": [ - "ierc721", - "ierc721enumerable", - "ierc721metadata" + "erc20" ] }, { @@ -960,7 +876,44 @@ "type": "contract", "dependencies": [ "erc20burnable" - ] + ], + "requiredParameters": { + "ERC20BurnableMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } + }, + { + "id": "erc20detailed", + "name": "token/ERC20/ERC20Detailed.sol", + "hash": "baa9ba5c46a405234eb7159f18455f71f8127c29", + "type": "abstractContract", + "dependencies": [ + "ierc20" + ], + "requiredParameters": { + "ERC20Detailed": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "decimals", + "type": "uint8" + } + ] + } }, { "id": "erc20detailedmock", @@ -970,7 +923,23 @@ "dependencies": [ "erc20", "erc20detailed" - ] + ], + "requiredParameters": { + "ERC20DetailedMock": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "decimals", + "type": "uint8" + } + ] + } }, { "id": "erc20metadata", @@ -979,7 +948,15 @@ "type": "contract", "dependencies": [ "ierc20" - ] + ], + "requiredParameters": { + "ERC20Metadata": [ + { + "name": "tokenURI_", + "type": "string" + } + ] + } }, { "id": "erc20metadatamock", @@ -989,7 +966,15 @@ "dependencies": [ "erc20", "erc20metadata" - ] + ], + "requiredParameters": { + "ERC20MetadataMock": [ + { + "name": "tokenURI", + "type": "string" + } + ] + } }, { "id": "minterrolemock", @@ -1017,6 +1002,28 @@ "type": "contract", "dependencies": [ "erc20" + ], + "requiredParameters": { + "ERC20Mock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } + }, + { + "id": "erc20pausable", + "name": "token/ERC20/ERC20Pausable.sol", + "hash": "3304cc033cb225a23e5e38b058f540af845d703a", + "type": "contract", + "dependencies": [ + "erc20", + "pausable" ] }, { @@ -1036,7 +1043,19 @@ "dependencies": [ "erc20pausable", "pauserrolemock" - ] + ], + "requiredParameters": { + "ERC20PausableMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint" + } + ] + } }, { "id": "erc20snapshot", @@ -1057,6 +1076,151 @@ "type": "contract", "dependencies": [ "erc20snapshot" + ], + "requiredParameters": { + "ERC20SnapshotMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } + }, + { + "id": "ierc721", + "name": "token/ERC721/IERC721.sol", + "hash": "960c1f902529069ee5352fdbb9d75a54b42eaf9c", + "type": "abstractContract", + "dependencies": [ + "ierc165" + ] + }, + { + "id": "ierc721receiver", + "name": "token/ERC721/IERC721Receiver.sol", + "hash": "b0618c0ec70781add71d928c8c0b633630a6d7a9", + "type": "abstractContract" + }, + { + "id": "erc721", + "name": "token/ERC721/ERC721.sol", + "hash": "1c9a79de3eee0772a59b57d0ff865fe10b1094f6", + "type": "contract", + "dependencies": [ + "ierc721", + "ierc721receiver", + "safemath", + "address", + "counters", + "erc165" + ] + }, + { + "id": "ierc721enumerable", + "name": "token/ERC721/IERC721Enumerable.sol", + "hash": "4c1dfb564a11820ec63b890aaf45a1ab349bd72e", + "type": "abstractContract", + "dependencies": [ + "ierc721" + ] + }, + { + "id": "erc721enumerable", + "name": "token/ERC721/ERC721Enumerable.sol", + "hash": "ed987e3ab3f188e9af282e0df30677ddc2eb9e87", + "type": "contract", + "dependencies": [ + "ierc721enumerable", + "erc721", + "erc165" + ] + }, + { + "id": "ierc721metadata", + "name": "token/ERC721/IERC721Metadata.sol", + "hash": "05835aff87f27c8b3195eac2b9d06d3ed0c5489e", + "type": "abstractContract", + "dependencies": [ + "ierc721" + ] + }, + { + "id": "erc721metadata", + "name": "token/ERC721/ERC721Metadata.sol", + "hash": "ce263ddc7f40803dfe70cf13eeb324ccd83860bd", + "type": "contract", + "dependencies": [ + "erc721", + "ierc721metadata", + "erc165" + ], + "requiredParameters": { + "ERC721Metadata": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } + }, + { + "id": "erc721full", + "name": "token/ERC721/ERC721Full.sol", + "hash": "308ea5265964963799ef3d40042d8fa3e5261c87", + "type": "contract", + "dependencies": [ + "erc721", + "erc721enumerable", + "erc721metadata" + ], + "requiredParameters": { + "ERC721Full": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } + }, + { + "id": "erc721mintable", + "name": "token/ERC721/ERC721Mintable.sol", + "hash": "98cd7a3a8ddf9898fe8e9c5800063ecf27b46114", + "type": "contract", + "dependencies": [ + "erc721", + "minterrole" + ] + }, + { + "id": "erc721metadatamintable", + "name": "token/ERC721/ERC721MetadataMintable.sol", + "hash": "a1421d1dd64d3c2b5546e5f710350fc708439af6", + "type": "abstractContract", + "dependencies": [ + "erc721metadata", + "minterrole" + ] + }, + { + "id": "erc721burnable", + "name": "token/ERC721/ERC721Burnable.sol", + "hash": "562a17e8e409981c795a5b84798a3b63cb24f465", + "type": "contract", + "dependencies": [ + "erc721" ] }, { @@ -1069,7 +1233,19 @@ "erc721mintable", "erc721metadatamintable", "erc721burnable" - ] + ], + "requiredParameters": { + "ERC721FullMock": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } }, { "id": "erc721mintableburnableimpl", @@ -1092,6 +1268,16 @@ "erc721" ] }, + { + "id": "erc721pausable", + "name": "token/ERC721/ERC721Pausable.sol", + "hash": "7cfd39ae6cd32c6f48c20654cacae74158b8ef0e", + "type": "contract", + "dependencies": [ + "erc721", + "pausable" + ] + }, { "id": "erc721pausablemock", "name": "mocks/ERC721PausableMock.sol", @@ -1109,7 +1295,68 @@ "type": "contract", "dependencies": [ "ierc721receiver" - ] + ], + "requiredParameters": { + "ERC721ReceiverMock": [ + { + "name": "retval", + "type": "bytes4" + }, + { + "name": "reverts", + "type": "bool" + } + ] + } + }, + { + "id": "ierc777", + "name": "token/ERC777/IERC777.sol", + "hash": "776de1dc9ba0e66afa949e98b07ae564445ee973", + "type": "interface" + }, + { + "id": "ierc777recipient", + "name": "token/ERC777/IERC777Recipient.sol", + "hash": "a46b30a9f659ff492e3c169b3ef49bc87c5cfea3", + "type": "interface" + }, + { + "id": "ierc777sender", + "name": "token/ERC777/IERC777Sender.sol", + "hash": "6a158c52d2e41e9b23f31dc3f979e1d94aed50d5", + "type": "interface" + }, + { + "id": "erc777", + "name": "token/ERC777/ERC777.sol", + "hash": "f857512c83bf6245f4a25db7dc2e2f16c1017f2b", + "type": "contract", + "dependencies": [ + "ierc777", + "ierc777recipient", + "ierc777sender", + "ierc20", + "safemath", + "address", + "ierc1820registry" + ], + "requiredParameters": { + "ERC777": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "defaultOperators", + "type": "address[]" + } + ] + } }, { "id": "erc777mock", @@ -1118,7 +1365,31 @@ "type": "contract", "dependencies": [ "erc777" - ] + ], + "requiredParameters": { + "ERC777Mock": [ + { + "name": "initialHolder", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "defaultOperators", + "type": "address[]" + } + ] + } }, { "id": "erc777senderrecipientmock", @@ -1141,7 +1412,31 @@ "dependencies": [ "ierc20", "finalizablecrowdsale" - ] + ], + "requiredParameters": { + "FinalizableCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "increasingpricecrowdsaleimpl", @@ -1151,7 +1446,35 @@ "dependencies": [ "increasingpricecrowdsale", "safemath" - ] + ], + "requiredParameters": { + "IncreasingPriceCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "initialRate", + "type": "uint256" + }, + { + "name": "finalRate", + "type": "uint256" + } + ] + } }, { "id": "individuallycappedcrowdsaleimpl", @@ -1162,7 +1485,23 @@ "ierc20", "individuallycappedcrowdsale", "capperrolemock" - ] + ], + "requiredParameters": { + "IndividuallyCappedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "mathmock", @@ -1190,7 +1529,29 @@ "dependencies": [ "erc20mintable", "mintedcrowdsale" - ] + ], + "requiredParameters": { + "MintedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "ERC20Mintable" + } + ] + } + }, + { + "id": "ownable", + "name": "ownership/Ownable.sol", + "hash": "9c7b5dedc4e71ee1c6291c4add8d3b37f6d5f880", + "type": "abstractContract" }, { "id": "ownableinterfaceid", @@ -1218,7 +1579,23 @@ "dependencies": [ "erc20", "pausablecrowdsale" - ] + ], + "requiredParameters": { + "PausableCrowdsaleImpl": [ + { + "name": "_rate", + "type": "uint256" + }, + { + "name": "_wallet", + "type": "address" + }, + { + "name": "_token", + "type": "ERC20" + } + ] + } }, { "id": "pausablemock", @@ -1238,6 +1615,39 @@ "dependencies": [ "ierc20", "postdeliverycrowdsale" + ], + "requiredParameters": { + "PostDeliveryCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } + }, + { + "id": "pullpayment", + "name": "payment/PullPayment.sol", + "hash": "9e326514ba11403b1f485ecb771fd3eba5a66f0a", + "type": "abstractContract", + "dependencies": [ + "escrow" ] }, { @@ -1273,7 +1683,35 @@ "dependencies": [ "ierc20", "refundablecrowdsale" - ] + ], + "requiredParameters": { + "RefundableCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "goal", + "type": "uint256" + } + ] + } }, { "id": "refundablepostdeliverycrowdsaleimpl", @@ -1283,7 +1721,35 @@ "dependencies": [ "ierc20", "refundablepostdeliverycrowdsale" - ] + ], + "requiredParameters": { + "RefundablePostDeliveryCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "goal", + "type": "uint256" + } + ] + } }, { "id": "rolesmock", @@ -1298,11 +1764,24 @@ "id": "safeerc20helper", "name": "mocks/SafeERC20Helper.sol", "hash": "b36dc35a7f97e5b91d28c9c70dcd0e4a6e750d37", - "type": "contract", + "contracts": [ + "ERC20ReturnFalseMock", + "ERC20ReturnTrueMock", + "ERC20NoReturnMock", + "SafeERC20Wrapper" + ], "dependencies": [ "ierc20", "safeerc20" - ] + ], + "requiredParameters": { + "SafeERC20Wrapper": [ + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "safemathmock", @@ -1374,7 +1853,31 @@ "dependencies": [ "ierc20", "timedcrowdsale" - ] + ], + "requiredParameters": { + "TimedCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "whitelistadminrolemock", @@ -1394,7 +1897,23 @@ "ierc20", "whitelistcrowdsale", "crowdsale" - ] + ], + "requiredParameters": { + "WhitelistCrowdsaleImpl": [ + { + "name": "_rate", + "type": "uint256" + }, + { + "name": "_wallet", + "type": "address" + }, + { + "name": "_token", + "type": "IERC20" + } + ] + } }, { "id": "whitelistedrolemock", @@ -1404,6 +1923,90 @@ "dependencies": [ "whitelistedrole" ] + }, + { + "id": "paymentsplitter", + "name": "payment/PaymentSplitter.sol", + "hash": "7064404485ace9a427a0293ee11087aa621293e0", + "type": "contract", + "dependencies": [ + "safemath" + ], + "requiredParameters": { + "PaymentSplitter": [ + { + "name": "payees", + "type": "address[]" + }, + { + "name": "shares", + "type": "uint256[]" + } + ] + } + }, + { + "id": "erc20capped", + "name": "token/ERC20/ERC20Capped.sol", + "hash": "36bc9a9b0c708bfae620011940b3263f2e700ca6", + "type": "contract", + "dependencies": [ + "erc20mintable" + ], + "requiredParameters": { + "ERC20Capped": [ + { + "name": "cap", + "type": "uint256" + } + ] + } + }, + { + "id": "tokentimelock", + "name": "token/ERC20/TokenTimelock.sol", + "hash": "23dc2f9c3b4ac524a7402b77c8cc3eb8f1e16989", + "type": "contract", + "dependencies": [ + "safeerc20" + ], + "requiredParameters": { + "TokenTimelock": [ + { + "name": "token", + "type": "IERC20" + }, + { + "name": "beneficiary", + "type": "address" + }, + { + "name": "releaseTime", + "type": "uint256" + } + ] + } + }, + { + "id": "erc721holder", + "name": "token/ERC721/ERC721Holder.sol", + "hash": "bcc10d7e225495d51f897f31254cc128dde58669", + "type": "contract", + "dependencies": [ + "ierc721receiver" + ] + }, + { + "id": "ierc721full", + "name": "token/ERC721/IERC721Full.sol", + "hash": "e543aefeb38ae89d3cad665e81a89ab9d4d59dac", + "type": "abstractContract", + "dependencies": [ + "ierc721", + "ierc721enumerable", + "ierc721metadata" + ] } - ] -} + ], + "openZeppelinVersion": "2.3.0" +} \ No newline at end of file diff --git a/src/services/openZeppelin/manifest-2.4.0.json b/src/services/openZeppelin/manifest-2.4.0.json index 100b9ef..c8b4bc2 100644 --- a/src/services/openZeppelin/manifest-2.4.0.json +++ b/src/services/openZeppelin/manifest-2.4.0.json @@ -3,7 +3,6 @@ "baseUri": "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/", "targetPoint": "cdf655f770cf8412fbd4d6aef3c55194a1233ef1/contracts/", "apiDocumentationBaseUri": "https://docs.openzeppelin.com/contracts/2.x/api", - "openZeppelinVersion": "2.4.0", "categories": [ { "name": "Gas Station Network", @@ -313,7 +312,23 @@ "type": "abstractContract", "dependencies": [ "ierc20" - ] + ], + "requiredParameters": { + "ERC20Detailed": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "decimals", + "type": "uint8" + } + ] + } }, { "id": "gsnrecipienterc20fee", @@ -330,7 +345,33 @@ "safeerc20", "erc20", "erc20detailed" - ] + ], + "requiredParameters": { + "GSNRecipientERC20Fee": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ], + "__unstable__ERC20PrimaryAdmin": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "decimals", + "type": "uint8" + } + ] + } }, { "id": "ecdsa", @@ -346,7 +387,15 @@ "dependencies": [ "gsnrecipient", "ecdsa" - ] + ], + "requiredParameters": { + "GSNRecipientSignature": [ + { + "name": "trustedSigner", + "type": "address" + } + ] + } }, { "id": "roles", @@ -432,7 +481,23 @@ "safemath", "safeerc20", "reentrancyguard" - ] + ], + "requiredParameters": { + "Crowdsale": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "timedcrowdsale", @@ -442,7 +507,19 @@ "dependencies": [ "safemath", "crowdsale" - ] + ], + "requiredParameters": { + "TimedCrowdsale": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + } + ] + } }, { "id": "finalizablecrowdsale", @@ -496,7 +573,15 @@ "type": "contract", "dependencies": [ "conditionalescrow" - ] + ], + "requiredParameters": { + "RefundEscrow": [ + { + "name": "beneficiary", + "type": "address" + } + ] + } }, { "id": "refundablecrowdsale", @@ -508,7 +593,15 @@ "safemath", "finalizablecrowdsale", "refundescrow" - ] + ], + "requiredParameters": { + "RefundableCrowdsale": [ + { + "name": "goal", + "type": "uint256" + } + ] + } }, { "id": "refundablepostdeliverycrowdsale", @@ -537,7 +630,15 @@ "safeerc20", "safemath", "math" - ] + ], + "requiredParameters": { + "AllowanceCrowdsale": [ + { + "name": "tokenWallet", + "type": "address" + } + ] + } }, { "id": "erc20mintable", @@ -567,7 +668,19 @@ "dependencies": [ "timedcrowdsale", "safemath" - ] + ], + "requiredParameters": { + "IncreasingPriceCrowdsale": [ + { + "name": "initialRate", + "type": "uint256" + }, + { + "name": "finalRate", + "type": "uint256" + } + ] + } }, { "id": "cappedcrowdsale", @@ -577,7 +690,15 @@ "dependencies": [ "safemath", "crowdsale" - ] + ], + "requiredParameters": { + "CappedCrowdsale": [ + { + "name": "cap", + "type": "uint256" + } + ] + } }, { "id": "individuallycappedcrowdsale", @@ -685,7 +806,27 @@ "dependencies": [ "ierc20", "allowancecrowdsale" - ] + ], + "requiredParameters": { + "AllowanceCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "tokenWallet", + "type": "address" + } + ] + } }, { "id": "arrays", @@ -703,7 +844,15 @@ "type": "contract", "dependencies": [ "arrays" - ] + ], + "requiredParameters": { + "ArraysImpl": [ + { + "name": "_array", + "type": "uint256[]" + } + ] + } }, { "id": "cappedcrowdsaleimpl", @@ -713,7 +862,27 @@ "dependencies": [ "ierc20", "cappedcrowdsale" - ] + ], + "requiredParameters": { + "CappedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "cap", + "type": "uint256" + } + ] + } }, { "id": "capperrolemock", @@ -770,7 +939,23 @@ "type": "contract", "dependencies": [ "crowdsale" - ] + ], + "requiredParameters": { + "CrowdsaleMock": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "ecdsamock", @@ -791,7 +976,15 @@ ], "dependencies": [ "ierc165" - ] + ], + "requiredParameters": { + "ERC165InterfacesSupported": [ + { + "name": "interfaceIds", + "type": "bytes4[]" + } + ] + } }, { "id": "erc165notsupported", @@ -843,7 +1036,19 @@ "type": "contract", "dependencies": [ "erc20burnable" - ] + ], + "requiredParameters": { + "ERC20BurnableMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } }, { "id": "erc20detailedmock", @@ -853,7 +1058,23 @@ "dependencies": [ "erc20", "erc20detailed" - ] + ], + "requiredParameters": { + "ERC20DetailedMock": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "decimals", + "type": "uint8" + } + ] + } }, { "id": "erc20metadata", @@ -862,7 +1083,15 @@ "type": "contract", "dependencies": [ "ierc20" - ] + ], + "requiredParameters": { + "ERC20Metadata": [ + { + "name": "tokenURI_", + "type": "string" + } + ] + } }, { "id": "erc20metadatamock", @@ -872,7 +1101,15 @@ "dependencies": [ "erc20", "erc20metadata" - ] + ], + "requiredParameters": { + "ERC20MetadataMock": [ + { + "name": "tokenURI", + "type": "string" + } + ] + } }, { "id": "minterrolemock", @@ -900,7 +1137,19 @@ "type": "contract", "dependencies": [ "erc20" - ] + ], + "requiredParameters": { + "ERC20Mock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } }, { "id": "erc20pausable", @@ -929,7 +1178,19 @@ "dependencies": [ "erc20pausable", "pauserrolemock" - ] + ], + "requiredParameters": { + "ERC20PausableMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } }, { "id": "erc20snapshot", @@ -950,7 +1211,19 @@ "type": "contract", "dependencies": [ "erc20snapshot" - ] + ], + "requiredParameters": { + "ERC20SnapshotMock": [ + { + "name": "initialAccount", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + } + ] + } }, { "id": "ierc721", @@ -1022,7 +1295,19 @@ "erc721", "ierc721metadata", "erc165" - ] + ], + "requiredParameters": { + "ERC721Metadata": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } }, { "id": "erc721full", @@ -1033,7 +1318,19 @@ "erc721", "erc721enumerable", "erc721metadata" - ] + ], + "requiredParameters": { + "ERC721Full": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } }, { "id": "erc721mintable", @@ -1075,7 +1372,19 @@ "erc721mintable", "erc721metadatamintable", "erc721burnable" - ] + ], + "requiredParameters": { + "ERC721FullMock": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } }, { "id": "erc721gsnrecipientmock", @@ -1086,7 +1395,15 @@ "erc721", "gsnrecipient", "gsnrecipientsignature" - ] + ], + "requiredParameters": { + "ERC721GSNRecipientMock": [ + { + "name": "trustedSigner", + "type": "address" + } + ] + } }, { "id": "erc721mintableburnableimpl", @@ -1136,7 +1453,19 @@ "type": "contract", "dependencies": [ "ierc721receiver" - ] + ], + "requiredParameters": { + "ERC721ReceiverMock": [ + { + "name": "retval", + "type": "bytes4" + }, + { + "name": "reverts", + "type": "bool" + } + ] + } }, { "id": "ierc777", @@ -1170,7 +1499,23 @@ "safemath", "address", "ierc1820registry" - ] + ], + "requiredParameters": { + "ERC777": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "defaultOperators", + "type": "address[]" + } + ] + } }, { "id": "erc777mock", @@ -1180,7 +1525,31 @@ "dependencies": [ "context", "erc777" - ] + ], + "requiredParameters": { + "ERC777Mock": [ + { + "name": "initialHolder", + "type": "address" + }, + { + "name": "initialBalance", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "defaultOperators", + "type": "address[]" + } + ] + } }, { "id": "erc777senderrecipientmock", @@ -1210,7 +1579,31 @@ "dependencies": [ "ierc20", "finalizablecrowdsale" - ] + ], + "requiredParameters": { + "FinalizableCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "gsnrecipienterc20feemock", @@ -1220,7 +1613,19 @@ "dependencies": [ "gsnrecipient", "gsnrecipienterc20fee" - ] + ], + "requiredParameters": { + "GSNRecipientERC20FeeMock": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ] + } }, { "id": "gsnrecipientmock", @@ -1240,7 +1645,15 @@ "dependencies": [ "gsnrecipient", "gsnrecipientsignature" - ] + ], + "requiredParameters": { + "GSNRecipientSignatureMock": [ + { + "name": "trustedSigner", + "type": "address" + } + ] + } }, { "id": "increasingpricecrowdsaleimpl", @@ -1250,7 +1663,35 @@ "dependencies": [ "increasingpricecrowdsale", "safemath" - ] + ], + "requiredParameters": { + "IncreasingPriceCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "initialRate", + "type": "uint256" + }, + { + "name": "finalRate", + "type": "uint256" + } + ] + } }, { "id": "individuallycappedcrowdsaleimpl", @@ -1261,7 +1702,23 @@ "ierc20", "individuallycappedcrowdsale", "capperrolemock" - ] + ], + "requiredParameters": { + "IndividuallyCappedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "mathmock", @@ -1289,7 +1746,23 @@ "dependencies": [ "erc20mintable", "mintedcrowdsale" - ] + ], + "requiredParameters": { + "MintedCrowdsaleImpl": [ + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "ERC20Mintable" + } + ] + } }, { "id": "ownable", @@ -1326,7 +1799,23 @@ "dependencies": [ "erc20", "pausablecrowdsale" - ] + ], + "requiredParameters": { + "PausableCrowdsaleImpl": [ + { + "name": "_rate", + "type": "uint256" + }, + { + "name": "_wallet", + "type": "address" + }, + { + "name": "_token", + "type": "ERC20" + } + ] + } }, { "id": "pausablemock", @@ -1346,7 +1835,31 @@ "dependencies": [ "ierc20", "postdeliverycrowdsale" - ] + ], + "requiredParameters": { + "PostDeliveryCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "pullpayment", @@ -1393,7 +1906,35 @@ "dependencies": [ "ierc20", "refundablecrowdsale" - ] + ], + "requiredParameters": { + "RefundableCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "goal", + "type": "uint256" + } + ] + } }, { "id": "refundablepostdeliverycrowdsaleimpl", @@ -1403,7 +1944,35 @@ "dependencies": [ "ierc20", "refundablepostdeliverycrowdsale" - ] + ], + "requiredParameters": { + "RefundablePostDeliveryCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + }, + { + "name": "goal", + "type": "uint256" + } + ] + } }, { "id": "rolesmock", @@ -1428,7 +1997,15 @@ "context", "ierc20", "safeerc20" - ] + ], + "requiredParameters": { + "SafeERC20Wrapper": [ + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "safemathmock", @@ -1495,7 +2072,31 @@ "dependencies": [ "ierc20", "timedcrowdsale" - ] + ], + "requiredParameters": { + "TimedCrowdsaleImpl": [ + { + "name": "openingTime", + "type": "uint256" + }, + { + "name": "closingTime", + "type": "uint256" + }, + { + "name": "rate", + "type": "uint256" + }, + { + "name": "wallet", + "type": "address" + }, + { + "name": "token", + "type": "IERC20" + } + ] + } }, { "id": "whitelistadminrolemock", @@ -1515,7 +2116,23 @@ "ierc20", "whitelistcrowdsale", "crowdsale" - ] + ], + "requiredParameters": { + "WhitelistCrowdsaleImpl": [ + { + "name": "_rate", + "type": "uint256" + }, + { + "name": "_wallet", + "type": "address" + }, + { + "name": "_token", + "type": "IERC20" + } + ] + } }, { "id": "whitelistedrolemock", @@ -1534,7 +2151,19 @@ "dependencies": [ "context", "safemath" - ] + ], + "requiredParameters": { + "PaymentSplitter": [ + { + "name": "payees", + "type": "address[]" + }, + { + "name": "shares", + "type": "uint256[]" + } + ] + } }, { "id": "erc20capped", @@ -1543,7 +2172,15 @@ "type": "contract", "dependencies": [ "erc20mintable" - ] + ], + "requiredParameters": { + "ERC20Capped": [ + { + "name": "cap", + "type": "uint256" + } + ] + } }, { "id": "tokentimelock", @@ -1552,7 +2189,23 @@ "type": "contract", "dependencies": [ "safeerc20" - ] + ], + "requiredParameters": { + "TokenTimelock": [ + { + "name": "token", + "type": "IERC20" + }, + { + "name": "beneficiary", + "type": "address" + }, + { + "name": "releaseTime", + "type": "uint256" + } + ] + } }, { "id": "erc721holder", @@ -1574,5 +2227,6 @@ "ierc721metadata" ] } - ] -} + ], + "openZeppelinVersion": "2.4.0" +} \ No newline at end of file diff --git a/src/services/openZeppelin/models.ts b/src/services/openZeppelin/models.ts index 97df695..f7a26da 100644 --- a/src/services/openZeppelin/models.ts +++ b/src/services/openZeppelin/models.ts @@ -24,6 +24,13 @@ export interface IOZAsset { type?: OZAssetType; contracts?: string[]; dependencies: string[]; + requiredParameters?: { [key: string]: IOZContractParameter[] }; +} + +export interface IOZContractParameter { + name: string; + type: string; + value?: string; } export interface IProjectMetadata { diff --git a/src/services/tree/TreeManager.ts b/src/services/tree/TreeManager.ts index df50a5a..ffc6159 100644 --- a/src/services/tree/TreeManager.ts +++ b/src/services/tree/TreeManager.ts @@ -6,6 +6,7 @@ import { Constants } from '../../Constants'; import { ItemFactory } from '../../Models'; import { AzureBlockchainService, + BlockchainDataManagerService, Command, IExtensionItem, InfuraService, @@ -101,7 +102,12 @@ class ExtensionTreeManager { infuraService = new InfuraService(); } - return [ azureBlockchainService, infuraService, localService]; + let bdmService = items.find((item) => item instanceof BlockchainDataManagerService); + if (!bdmService) { + bdmService = new BlockchainDataManagerService(); + } + + return [ azureBlockchainService, infuraService, localService, bdmService]; } } diff --git a/src/validators/solidityTypeValidation/index.ts b/src/validators/solidityTypeValidation/index.ts new file mode 100644 index 0000000..ca11121 --- /dev/null +++ b/src/validators/solidityTypeValidation/index.ts @@ -0,0 +1,85 @@ +import { Constants } from '../../Constants'; +import { bigIntMath } from '../../helpers/bigIntMath'; + +export function validateSolidityType(value: string, type: string): string | undefined { + if (type.match(Constants.validationRegexps.types.simpleMapping)) { + // Mapping type + return; + } + if (type.match(Constants.validationRegexps.types.simpleArray)) { + return validateSimpleArray(value, type.split('[]')[0]); + } + + return validateElementaryType(value, type); +} + +function validateElementaryType(value: string, type: string): string | undefined { + if (type.match(Constants.validationRegexps.types.solidityInteger)) { + return validateNumber(value, type); + } + + switch (type) { + case Constants.solidityTypes.string: + return; + case Constants.solidityTypes.bool: + return value === 'true' || value === 'false' ? undefined : Constants.validationMessages.valueShouldBeBool; + case Constants.solidityTypes.address: + return value.match(Constants.validationRegexps.types.solidityAddress) ? + undefined : + Constants.validationMessages.valueShouldBeSolidityAddress; + default: + // TODO: validate other types + return; + } +} + +function validateNumber(value: string, type: string): string | undefined { + if (!value.match(Constants.validationRegexps.onlyNumber)) { + return Constants.validationMessages.valueShouldBeNumber; + } + + if (type.match(Constants.validationRegexps.types.solidityUint)) { + const pow = type.split(Constants.solidityTypes.uint)[1]; + const maxUint = bigIntMath.pow(BigInt(2), parseInt(pow, 10)) - BigInt(1); + const valueAsNumber = BigInt(parseInt(value, 10)); + if (valueAsNumber < BigInt(0) || valueAsNumber > maxUint) { + return Constants.validationMessages.valueShouldBePositiveAndCanSafelyStoreUpToBits(pow); + } + } + + if (type.match(Constants.validationRegexps.types.solidityInt)) { + const pow = type.split(Constants.solidityTypes.int)[1]; + const maxInt = bigIntMath.pow(BigInt(2), parseInt(pow, 10) - 1) - BigInt(1); + const minInt = -maxInt; + const valueAsNumber = BigInt(parseInt(value, 10)); + if (valueAsNumber < minInt || valueAsNumber > maxInt) { + return Constants.validationMessages.valueCanSafelyStoreUpToBits(pow); + } + } + + return; +} + +function validateSimpleArray(value: string, elementsType: string): string | undefined { + if (!value.match(Constants.validationRegexps.array)) { + return Constants.validationMessages.valueShouldBeArray; + } + + const values = value.slice(1, value.length - 1).split(','); + + if (values.length === 0 || + (values.length === 1 && values[0] === '')) { + return; + } + + const invalidValues = values.filter((element) => { + const result = validateSolidityType(element.trim(), elementsType); + return !!result; + }); + + if (invalidValues.length > 0) { + return Constants.validationMessages.arrayElementsShouldBeValid(elementsType); + } + + return; +} diff --git a/src/validators/validationFunctions/isConfirmationValue.ts b/src/validators/validationFunctions/isConfirmationValue.ts index 89ca737..dd424cd 100644 --- a/src/validators/validationFunctions/isConfirmationValue.ts +++ b/src/validators/validationFunctions/isConfirmationValue.ts @@ -15,7 +15,7 @@ export class IsConfirmationValue implements IRule { } public validate(value: string): string | null { - const isConfirmationValue = this.yesNoOptions.includes(value.toLowerCase()); + const isConfirmationValue = this.yesNoOptions.map((option) => option.toLowerCase()).includes(value.toLowerCase()); return isConfirmationValue ? null : Constants.validationMessages.invalidConfirmationResult; } } diff --git a/test/AzureBlockchainServiceClient.int.test.ts b/test/AzureBlockchainServiceClient.int.test.ts new file mode 100644 index 0000000..a160d23 --- /dev/null +++ b/test/AzureBlockchainServiceClient.int.test.ts @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as assert from 'assert'; +import { ServiceClientCredentials } from 'ms-rest'; +import * as sinon from 'sinon'; +import * as uuid from 'uuid'; +import * as vscode from 'vscode'; +import { AzureBlockchainServiceClient } from '../src/ARMBlockchain'; + +describe('Integration tests for AzureBlockchainServiceClient', () => { + let credentials: ServiceClientCredentials; + let subscriptionId: string; + let resourceGroup: string; + let baseUri: string; + let memberName: string; + let bdmName: string; + let transactionNode: string; + const azureBlockchainServiceClient = require('../src/ARMBlockchain/AzureBlockchainServiceClient'); + const defaultResponseBody = '{ "message": "default response body" }'; + let callbackFunction: (error: Error | null, result?: any) => void; + + const incorrectResponseBody = uuid.v4(); + let windowMock: sinon.SinonMock; + let showErrorMessageMock: sinon.SinonExpectation; + let pipelineMock: sinon.SinonStub | sinon.SinonStub; + let serviceClient: AzureBlockchainServiceClient; + let callbackFunctionSpy: any; + + before(() => { + credentials = { + signRequest: () => undefined, + }; + callbackFunction = (_error: Error | null, _result?: any) => undefined; + }); + + beforeEach(() => { + subscriptionId = uuid.v4(); + resourceGroup = uuid.v4(); + baseUri = uuid.v4(); + memberName = uuid.v4(); + bdmName = uuid.v4(); + transactionNode = uuid.v4(); + sinon.stub(azureBlockchainServiceClient.__proto__, 'constructor'); + + windowMock = sinon.mock(vscode.window); + showErrorMessageMock = windowMock.expects('showErrorMessage'); + const specialOptions = { + acceptLanguage: uuid.v4(), + generateClientRequestId: true, + }; + serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( + credentials, + subscriptionId, + resourceGroup, + baseUri, + specialOptions, + ); + // @ts-ignore + pipelineMock = sinon.stub(serviceClient, 'pipeline'); + callbackFunctionSpy = sinon.spy(callbackFunction); + }); + + afterEach(() => { + sinon.restore(); + }); + + function assertRequestFailed(error: any, callbackSpy: sinon.SinonSpy<[Error | null, any?], void>): void { + assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); + assert.strictEqual( + showErrorMessageMock.calledOnceWithExactly(error.message), + true, + 'showErrorMessage should called once with correct arguments'); + assert.strictEqual( + callbackSpy.calledOnceWithExactly(error as Error), + true, + 'callbackFunction should called once with correct arguments'); + } + + function assertResponseNotSuccess( + callbackSpy: sinon.SinonSpy<[Error | null, any?], void>, + pipelineCallbackSpy: sinon.SinonSpy) + : void { + assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); + assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); + assert.strictEqual(callbackSpy.calledOnce, true, 'callbackFunction should called once'); + assert.strictEqual( + callbackSpy.args[0][0] instanceof Error, + true, + 'callbackFunction should called with correct arguments'); + assert.strictEqual(pipelineCallbackSpy.calledOnce, true, 'pipelineCallback should called once'); + assert.strictEqual( + pipelineCallbackSpy.args[0][0], + null, + 'callback function should called with correct arguments'); + } + + function assertResponseSuccess( + callbackSpy: sinon.SinonSpy<[Error | null, any?], void>, + pipelineCallbackSpy: sinon.SinonSpy, + parsedResult: any) + : void { + assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); + assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); + assert.strictEqual( + callbackSpy.calledOnceWithExactly(null, parsedResult), + true, + 'callbackFunction should called once with correct arguments'); + assert.strictEqual(pipelineCallbackSpy.calledOnce, true, 'pipelineCallback should called once'); + assert.strictEqual( + pipelineCallbackSpy.args[0][0], + null, + 'callback function should called with correct arguments'); + } + + const listOfMethod = [ + { callback: async () => await serviceClient.getMembers('consortiumName', callbackFunctionSpy), + methodName: 'getMembers' }, + { callback: async () => await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy), + methodName: 'getTransactionNodes' }, + { callback: async () => + await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy), + methodName: 'getTransactionNodeAccessKeys' }, + { callback: async () => await serviceClient.getSkus(callbackFunctionSpy), + methodName: 'getSkus' }, + { callback: async () => await serviceClient.getConsortia(callbackFunctionSpy), + methodName: 'getConsortia' }, + { callback: async () => await serviceClient.getBlockchainDataManagers(callbackFunctionSpy), + methodName: 'getBlockchainDataManagers' }, + { callback: async () => await serviceClient.getBlockchainDataManagerApplications(bdmName, callbackFunctionSpy), + methodName: 'getBlockchainDataManagerApplications' }, + ]; + + listOfMethod.forEach((method) => { + describe(method.methodName, () => { + it('shows error when request failed.', async () => { + // Arrange + const response = sinon.stub(); + const error = { message: uuid.v4() }; + + pipelineMock.callsFake((...args: any[]): {} => { + return args[1](error, response, defaultResponseBody); + }); + + // Act + await method.callback(); + + // Assert + assertRequestFailed(error, callbackFunctionSpy); + }); + + describe('throws error when response is not success.', () => { + const responseData = [ + [400, defaultResponseBody], + [103, defaultResponseBody], + [530, defaultResponseBody], + [200, incorrectResponseBody]]; + responseData.forEach(async (response) => { + it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, + async () => { + // Arrange + const res = {statusCode: response[0]}; + let pipelineCallbackSpy: any; + + pipelineMock.callsFake((...args: any[]): {} => { + pipelineCallbackSpy = sinon.spy(args[1]); + return pipelineCallbackSpy(null, res, response[1]); + }); + + // Act + await method.callback(); + + // Assert + assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); + }); + }); + }); + + it('does not throw error when response is success.', async () => { + // Arrange + const res = {statusCode: 200}; + const parsedResult = JSON.parse(defaultResponseBody); + let pipelineCallbackSpy: any; + + pipelineMock.callsFake((...args: any[]): {} => { + pipelineCallbackSpy = sinon.spy(args[1]); + return pipelineCallbackSpy(null, res, defaultResponseBody); + }); + + // Act + await method.callback(); + + // Assert + assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); + }); + }); + }); +}); diff --git a/test/AzureBlockchainServiceClient.test.ts b/test/AzureBlockchainServiceClient.test.ts index 45cc571..5b3fabd 100644 --- a/test/AzureBlockchainServiceClient.test.ts +++ b/test/AzureBlockchainServiceClient.test.ts @@ -7,855 +7,303 @@ import * as msrestazure from 'ms-rest-azure'; import * as sinon from 'sinon'; import * as uuid from 'uuid'; import * as vscode from 'vscode'; -import { AzureBlockchainServiceClient } from '../src/ARMBlockchain'; +import { AzureBlockchainServiceClient } from '../src/ARMBlockchain/AzureBlockchainServiceClient'; import { Constants } from '../src/Constants'; import { vscodeEnvironment } from '../src/helpers'; import { Output } from '../src/Output'; -describe('AzureBlockchainServiceClient', () => { +describe('Unit tests for AzureBlockchainServiceClient', () => { let credentials: ServiceClientCredentials; let subscriptionId: string; let resourceGroup: string; let baseUri: string; - let apiVersion: string; let memberName: string; + let bdmName: string; let transactionNode: string; const azureBlockchainServiceClient = require('../src/ARMBlockchain/AzureBlockchainServiceClient'); const defaultResponseBody = '{ "message": "default response body" }'; let callbackFunction: (error: Error | null, result?: any) => void; + let callbackFunctionSpy: any; + let options: msrestazure.AzureServiceClientOptions; + let body: string; + let resultElement: string; before(() => { credentials = { signRequest: () => undefined, }; callbackFunction = (_error: Error | null, _result?: any) => undefined; + options = {}; + body = uuid.v4(); + resultElement = uuid.v4(); }); beforeEach(() => { subscriptionId = uuid.v4(); resourceGroup = uuid.v4(); baseUri = uuid.v4(); - apiVersion = uuid.v4(); memberName = uuid.v4(); + bdmName = uuid.v4(); transactionNode = uuid.v4(); sinon.stub(azureBlockchainServiceClient.__proto__, 'constructor'); + + callbackFunctionSpy = sinon.spy(callbackFunction); }); afterEach(() => { sinon.restore(); }); - describe('Unit tests', () => { - let options: msrestazure.AzureServiceClientOptions; - let body: string; - let resultElement: string; + describe('constructor', () => { + it('AzureBlockchainServiceClient was created.', async () => { + // Arrange, Act + const serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( + credentials, + subscriptionId, + resourceGroup, + baseUri, + options, + ); - before(() => { - options = {}; - body = uuid.v4(); - resultElement = uuid.v4(); + // Assert + assert.notStrictEqual(serviceClient, undefined, 'serviceClient should not be undefined'); + assert.strictEqual( + serviceClient.constructor.name, + AzureBlockchainServiceClient.name, + `serviceClient name should be equal to ${AzureBlockchainServiceClient.name}`); }); - describe('constructor', () => { - it('AzureBlockchainServiceClient was created.', async () => { - // Arrange, Act - const serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( - credentials, - subscriptionId, - resourceGroup, - baseUri, - apiVersion, - options, - ); + describe('invalid subscriptionId', () => { + const invalidSubscriptions = [String.Empty, null, undefined, '']; + invalidSubscriptions.forEach(async (subscription) => { + it(`AzureBlockchainServiceClient constructor throws error when subscriptionId is ${subscription}.`, + async () => { + // Arrange + let serviceClient; - // Assert - assert.notStrictEqual(serviceClient, undefined, 'serviceClient should not be undefined'); - assert.strictEqual( - serviceClient.constructor.name, - AzureBlockchainServiceClient.name, - `serviceClient name should be equal to ${AzureBlockchainServiceClient.name}`); - }); + // Act + const action = () => { + serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( + credentials, + subscription, + resourceGroup, + baseUri, + options, + ); + }; - describe('invalid subscriptionId', () => { - const invalidSubscriptions = [String.Empty, null, undefined, '']; - invalidSubscriptions.forEach(async (subscription) => { - it(`AzureBlockchainServiceClient constructor throws error when subscriptionId is ${subscription}.`, - async () => { - // Arrange - let serviceClient; - - // Act - const action = () => { - serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( - credentials, - subscription, - resourceGroup, - baseUri, - apiVersion, - options, - ); - }; - - // Assert - assert.throws( - action, - Error, - Constants.errorMessageStrings.VariableShouldBeDefined('subscriptionId')); - assert.strictEqual(serviceClient, undefined, 'serviceClient should be undefined'); - }); - }); - }); - - describe('invalid credentials', () => { - const invalidCredentials = [null, undefined]; - invalidCredentials.forEach(async (credential) => { - it(`AzureBlockchainServiceClient constructor throws error when credentials is ${credential}.`, - async () => { - // Arrange - let serviceClient; - - // Act - const action = () => { - serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( - credential, - subscriptionId, - resourceGroup, - baseUri, - apiVersion, - options, - ); - }; - - // Assert - assert.throws( - action, - Error, - Constants.errorMessageStrings.VariableShouldBeDefined('credentials')); - assert.strictEqual(serviceClient, undefined, 'serviceClient should be undefined'); - }); + // Assert + assert.throws( + action, + Error, + Constants.errorMessageStrings.VariableShouldBeDefined('subscriptionId')); + assert.strictEqual(serviceClient, undefined, 'serviceClient should be undefined'); }); }); }); - describe('Public methods.', () => { - let serviceClient: AzureBlockchainServiceClient; - let pipelineMock: sinon.SinonStub | sinon.SinonStub; - let sendRequestToAzureMock: sinon.SinonStub; - let outputMock: sinon.SinonMock; - let outputLineMock: sinon.SinonExpectation; - let openExternalSpy: sinon.SinonSpy<[vscode.Uri], Thenable>; - let windowMock: sinon.SinonMock; - let showErrorMessageMock: sinon.SinonExpectation; + describe('invalid credentials', () => { + const invalidCredentials = [null, undefined]; + invalidCredentials.forEach(async (credential) => { + it(`AzureBlockchainServiceClient constructor throws error when credentials is ${credential}.`, + async () => { + // Arrange + let serviceClient; - beforeEach(() => { - serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( - credentials, - subscriptionId, - resourceGroup, - baseUri, - apiVersion, - options, - ); - // @ts-ignore - pipelineMock = sinon.stub(serviceClient, 'pipeline'); - sendRequestToAzureMock = sinon.stub(serviceClient, 'sendRequestToAzure' as any); - openExternalSpy = sinon.stub(vscodeEnvironment, 'openExternal'); - outputMock = sinon.mock(Output); - outputLineMock = outputMock.expects('outputLine'); - windowMock = sinon.mock(vscode.window); - showErrorMessageMock = windowMock.expects('showErrorMessage'); - }); + // Act + const action = () => { + serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( + credential, + subscriptionId, + resourceGroup, + baseUri, + options, + ); + }; - afterEach(() => { - sendRequestToAzureMock.restore(); - pipelineMock.restore(); - openExternalSpy.restore(); - outputMock.restore(); - windowMock.restore(); - }); - - it('createProject shows error when request failed.', async () => { - // Arrange - const response = sinon.stub(); - const error = { message: uuid.v4() }; - - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); + // Assert + assert.throws( + action, + Error, + Constants.errorMessageStrings.VariableShouldBeDefined('credentials')); + assert.strictEqual(serviceClient, undefined, 'serviceClient should be undefined'); }); - - // Act - await serviceClient.createConsortium(memberName, body); - - // Assert - assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); - assert.strictEqual( - outputLineMock.calledOnceWithExactly(Constants.outputChannel.azureBlockchainServiceClient, error.message), - true, - 'outputLine should called once with correct arguments'); - assert.strictEqual(openExternalSpy.notCalled, true, 'openExternal should not called'); - }); - - describe('createProject shows error when response is not success.', () => { - const statusCodes = [103, 300, 400, 498]; - statusCodes.forEach(async (statusCode) => { - it(`response status code is ${statusCode}.`, async () => { - // Arrange - const response = {statusCode, statusMessage: uuid.v4()}; - let callbackSpy: any; - pipelineMock.callsFake((...args: any[]): {} => { - callbackSpy = sinon.spy(args[1]); - return callbackSpy(null, response, defaultResponseBody); - }); - - // Act - await serviceClient.createConsortium(memberName, body); - - // Assert - assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); - assert.strictEqual(callbackSpy.args[0][0], null, 'callback function should called with correct arguments'); - assert.strictEqual( - outputLineMock.calledOnceWithExactly( - Constants.outputChannel.azureBlockchainServiceClient, - `${response.statusMessage}(${response.statusCode}): ${defaultResponseBody}`), - true, - 'outputLine should called once with correct arguments'); - assert.strictEqual(openExternalSpy.notCalled, true, 'openExternal should not called'); - assert.strictEqual( - showErrorMessageMock.calledOnceWithExactly( - Constants.executeCommandMessage.failedToRunCommand('CreateConsortium')), - true, - 'showErrorMessage should called once with correct arguments'); - }); - }); - }); - - describe('createProject does not show error when response is success.', () => { - const statusCodes = [200, 207, 226]; - statusCodes.forEach(async (statusCode) => { - it(`response status code is ${statusCode}.`, async () => { - // Arrange - const response = {statusCode, statusMessage: uuid.v4()}; - let callbackSpy: any; - pipelineMock.callsFake((...args: any[]): {} => { - callbackSpy = sinon.spy(args[1]); - return callbackSpy(null, response, defaultResponseBody); - }); - - // Act - await serviceClient.createConsortium(memberName, body); - - // Assert - assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); - assert.strictEqual(callbackSpy.args[0][0], null, 'callback function should called with correct arguments'); - assert.strictEqual(outputLineMock.notCalled, true, 'outputLine should not called'); - assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); - assert.strictEqual(openExternalSpy.calledOnce, true, 'openExternal should called once'); - assert.strictEqual( - openExternalSpy.args[0][0] instanceof vscode.Uri, - true, - 'openExternal should called with correct arguments'); - }); - }); - }); - - it('getMembers returns error.', async () => { - // Arrange - const consortiumName = 'consortiumName'; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const error = new Error(uuid.v4()); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](error); - }); - - // Act - await serviceClient.getMembers(consortiumName, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getMembers does not return error.', async () => { - // Arrange - const consortiumName = 'consortiumName'; - const callbackFunctionSpy = sinon.spy(callbackFunction); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](null, resultElement); - }); - - // Act - await serviceClient.getMembers(consortiumName, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, resultElement), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getConsortia returns error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - const error = new Error(uuid.v4()); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](error); - }); - - // Act - await serviceClient.getConsortia(callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getConsortia does not return error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](null, resultElement); - }); - - // Act - await serviceClient.getConsortia(callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, resultElement), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getTransactionNodes returns error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - const error = new Error(uuid.v4()); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](error); - }); - - // Act - await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getTransactionNodes does not return error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](null, resultElement); - }); - - // Act - await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, resultElement), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getTransactionNodeAccessKeys returns error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - const error = new Error(uuid.v4()); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](error); - }); - - // Act - await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getTransactionNodeAccessKeys does not return error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](null, resultElement); - }); - - // Act - await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, resultElement), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getSkus returns error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - const error = new Error(uuid.v4()); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](error); - }); - - // Act - await serviceClient.getSkus(callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error), - true, - 'callbackFunction should called once with correct arguments'); - }); - - it('getSkus does not return error.', async () => { - // Arrange - const callbackFunctionSpy = sinon.spy(callbackFunction); - sendRequestToAzureMock.callsFake((...args: any[]): {} => { - return args[1](null, resultElement); - }); - - // Act - await serviceClient.getSkus(callbackFunctionSpy); - - // Assert - assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, resultElement), - true, - 'callbackFunction should called once with correct arguments'); }); }); }); - describe('Integration tests', () => { - const incorrectResponseBody = uuid.v4(); + describe('Public methods.', () => { + let serviceClient: AzureBlockchainServiceClient; + let pipelineMock: sinon.SinonStub | sinon.SinonStub; + let sendRequestToAzureMock: sinon.SinonStub; + let outputMock: sinon.SinonMock; + let outputLineMock: sinon.SinonExpectation; + let openExternalSpy: sinon.SinonSpy<[vscode.Uri], Thenable>; let windowMock: sinon.SinonMock; let showErrorMessageMock: sinon.SinonExpectation; - let pipelineMock: sinon.SinonStub | sinon.SinonStub; - let serviceClient: AzureBlockchainServiceClient; beforeEach(() => { - windowMock = sinon.mock(vscode.window); - showErrorMessageMock = windowMock.expects('showErrorMessage'); - const specialOptions = { - acceptLanguage: uuid.v4(), - generateClientRequestId: true, - }; serviceClient = new azureBlockchainServiceClient.AzureBlockchainServiceClient( credentials, subscriptionId, resourceGroup, baseUri, - apiVersion, - specialOptions, + options, ); // @ts-ignore pipelineMock = sinon.stub(serviceClient, 'pipeline'); + sendRequestToAzureMock = sinon.stub(serviceClient, 'sendRequestToAzure' as any); + openExternalSpy = sinon.stub(vscodeEnvironment, 'openExternal'); + outputMock = sinon.mock(Output); + outputLineMock = outputMock.expects('outputLine'); + windowMock = sinon.mock(vscode.window); + showErrorMessageMock = windowMock.expects('showErrorMessage'); }); afterEach(() => { - windowMock.restore(); + sendRequestToAzureMock.restore(); pipelineMock.restore(); + openExternalSpy.restore(); + outputMock.restore(); + windowMock.restore(); }); - function assertRequestFailed(error: any, callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>): void { + it('createConsortium shows error when request failed.', async () => { + // Arrange + const response = sinon.stub(); + const error = { message: uuid.v4() }; + + pipelineMock.callsFake((...args: any[]): {} => { + return args[1](error, response, defaultResponseBody); + }); + + // Act + await serviceClient.createConsortium(memberName, body); + + // Assert assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); assert.strictEqual( - showErrorMessageMock.calledOnceWithExactly(error.message), + outputLineMock.calledOnceWithExactly(Constants.outputChannel.azureBlockchainServiceClient, error.message), true, - 'showErrorMessage should called once with correct arguments'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(error as Error), - true, - 'callbackFunction should called once with correct arguments'); - } + 'outputLine should called once with correct arguments'); + assert.strictEqual(openExternalSpy.notCalled, true, 'openExternal should not called'); + }); - function assertResponseNotSuccess( - callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>, - pipelineCallbackSpy: sinon.SinonSpy) - : void { - assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); - assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); - assert.strictEqual(callbackFunctionSpy.calledOnce, true, 'callbackFunction should called once'); - assert.strictEqual( - callbackFunctionSpy.args[0][0] instanceof Error, - true, - 'callbackFunction should called with correct arguments'); - assert.strictEqual(pipelineCallbackSpy.calledOnce, true, 'pipelineCallback should called once'); - assert.strictEqual( - pipelineCallbackSpy.args[0][0], - null, - 'callback function should called with correct arguments'); - } - - function assertResponseSuccess( - callbackFunctionSpy: sinon.SinonSpy<[Error | null, any?], void>, - pipelineCallbackSpy: sinon.SinonSpy, - parsedResult: any) - : void { - assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); - assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); - assert.strictEqual( - callbackFunctionSpy.calledOnceWithExactly(null, parsedResult), - true, - 'callbackFunction should called once with correct arguments'); - assert.strictEqual(pipelineCallbackSpy.calledOnce, true, 'pipelineCallback should called once'); - assert.strictEqual( - pipelineCallbackSpy.args[0][0], - null, - 'callback function should called with correct arguments'); - } - - describe('getMembers', () => { - it('shows error when request failed.', async () => { - // Arrange - const consortiumName = 'consortiumName'; - const response = sinon.stub(); - const error = { message: uuid.v4() }; - const callbackFunctionSpy = sinon.spy(callbackFunction); - - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); - }); - - // Act - await serviceClient.getMembers(consortiumName, callbackFunctionSpy); - - // Assert - assertRequestFailed(error, callbackFunctionSpy); - }); - - describe('throws error when response is not success.', () => { - const responseData = [ - [400, defaultResponseBody], - [103, defaultResponseBody], - [530, defaultResponseBody], - [200, incorrectResponseBody]]; - responseData.forEach(async (response) => { - it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, - async () => { - // Arrange - const consortiumName = 'consortiumName'; - const res = {statusCode: response[0]}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, response[1]); - }); - - // Act - await serviceClient.getMembers(consortiumName, callbackFunctionSpy); - - // Assert - assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); + describe('createConsortium shows error when response is not success.', () => { + const statusCodes = [103, 300, 400, 498]; + statusCodes.forEach(async (statusCode) => { + it(`response status code is ${statusCode}.`, async () => { + // Arrange + const response = {statusCode, statusMessage: uuid.v4()}; + let callbackSpy: any; + pipelineMock.callsFake((...args: any[]): {} => { + callbackSpy = sinon.spy(args[1]); + return callbackSpy(null, response, defaultResponseBody); }); + + // Act + await serviceClient.createConsortium(memberName, body); + + // Assert + assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); + assert.strictEqual(callbackSpy.args[0][0], null, 'callback function should called with correct arguments'); + assert.strictEqual( + outputLineMock.calledOnceWithExactly( + Constants.outputChannel.azureBlockchainServiceClient, + `${response.statusMessage}(${response.statusCode}): ${defaultResponseBody}`), + true, + 'outputLine should called once with correct arguments'); + assert.strictEqual(openExternalSpy.notCalled, true, 'openExternal should not called'); + assert.strictEqual( + showErrorMessageMock.calledOnceWithExactly( + Constants.executeCommandMessage.failedToRunCommand('CreateConsortium')), + true, + 'showErrorMessage should called once with correct arguments'); }); }); - - it('does not throw error when response is success.', async () => { - // Arrange - const consortiumName = 'consortiumName'; - const res = {statusCode: 200}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const parsedResult = JSON.parse(defaultResponseBody); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, defaultResponseBody); - }); - - // Act - await serviceClient.getMembers(consortiumName, callbackFunctionSpy); - - // Assert - assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); - }); }); - describe('getTransactionNodes', () => { - it('shows error when request failed.', async () => { - // Arrange - const response = sinon.stub(); - const error = { message: uuid.v4() }; - const callbackFunctionSpy = sinon.spy(callbackFunction); - - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); - }); - - // Act - await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy); - - // Assert - assertRequestFailed(error, callbackFunctionSpy); - }); - - describe('throws error when response is not success.', () => { - const responseData = [ - [400, defaultResponseBody], - [103, defaultResponseBody], - [530, defaultResponseBody], - [200, incorrectResponseBody]]; - responseData.forEach(async (response) => { - it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, - async () => { - // Arrange - const res = {statusCode: response[0]}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, response[1]); - }); - - // Act - await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy); - - // Assert - assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); + describe('createConsortium does not show error when response is success.', () => { + const statusCodes = [200, 207, 226]; + statusCodes.forEach(async (statusCode) => { + it(`response status code is ${statusCode}.`, async () => { + // Arrange + const response = {statusCode, statusMessage: uuid.v4()}; + let callbackSpy: any; + pipelineMock.callsFake((...args: any[]): {} => { + callbackSpy = sinon.spy(args[1]); + return callbackSpy(null, response, defaultResponseBody); }); + + // Act + await serviceClient.createConsortium(memberName, body); + + // Assert + assert.strictEqual(pipelineMock.calledOnce, true, 'pipeline should called once'); + assert.strictEqual(callbackSpy.args[0][0], null, 'callback function should called with correct arguments'); + assert.strictEqual(outputLineMock.notCalled, true, 'outputLine should not called'); + assert.strictEqual(showErrorMessageMock.notCalled, true, 'showErrorMessage should not called'); + assert.strictEqual(openExternalSpy.calledOnce, true, 'openExternal should called once'); + assert.strictEqual( + openExternalSpy.args[0][0] instanceof vscode.Uri, + true, + 'openExternal should called with correct arguments'); }); }); - - it('does not throw error when response is success.', async () => { - // Arrange - const res = {statusCode: 200}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const parsedResult = JSON.parse(defaultResponseBody); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, defaultResponseBody); - }); - - // Act - await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy); - - // Assert - assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); - }); }); - describe('getTransactionNodeAccessKeys', () => { - it('shows error when request failed.', async () => { - // Arrange - const response = sinon.stub(); - const error = { message: uuid.v4() }; - const callbackFunctionSpy = sinon.spy(callbackFunction); + const listOfMethod = [ + { callback: async () => await serviceClient.getMembers('consortiumName', callbackFunctionSpy), + methodName: 'getMembers' }, + { callback: async () => await serviceClient.getConsortia(callbackFunctionSpy), + methodName: 'getConsortia' }, + { callback: async () => await serviceClient.getTransactionNodes(memberName, callbackFunctionSpy), + methodName: 'getTransactionNodes' }, + { callback: async () => + await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy), + methodName: 'getTransactionNodeAccessKeys' }, + { callback: async () => await serviceClient.getSkus(callbackFunctionSpy), + methodName: 'getSkus' }, + { callback: async () => await serviceClient.getBlockchainDataManagers(callbackFunctionSpy), + methodName: 'getBlockchainDataManagers' }, + { callback: async () => await serviceClient.getBlockchainDataManagerApplications(bdmName, callbackFunctionSpy), + methodName: 'getBlockchainDataManagerpplications' }, + ]; - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); + listOfMethod.forEach((method) => { + it(`${method.methodName} returns error.`, async () => { + // Arrange + const error = new Error(uuid.v4()); + sendRequestToAzureMock.callsFake((...args: any[]): {} => { + return args[1](error); }); // Act - await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy); + await method.callback(); // Assert - assertRequestFailed(error, callbackFunctionSpy); + assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); + assert.strictEqual( + callbackFunctionSpy.calledOnceWithExactly(error), + true, + 'callbackFunction should called once with correct arguments'); }); - describe('throws error when response is not success.', () => { - const responseData = [ - [400, defaultResponseBody], - [103, defaultResponseBody], - [530, defaultResponseBody], - [200, incorrectResponseBody]]; - responseData.forEach(async (response) => { - it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, - async () => { - // Arrange - const res = {statusCode: response[0]}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, response[1]); - }); - - // Act - await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy); - - // Assert - assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); - }); - }); - }); - - it('does not throw error when response is success.', async () => { - // Arrange - const res = {statusCode: 200}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const parsedResult = JSON.parse(defaultResponseBody); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, defaultResponseBody); - }); - - // Act - await serviceClient.getTransactionNodeAccessKeys(memberName, transactionNode, callbackFunctionSpy); - - // Assert - assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); - }); - }); - - describe('getSkus', () => { - it('shows error when request failed.', async () => { + it(`${method.methodName} does not return error.`, async () => { // Arrange - const response = sinon.stub(); - const error = { message: uuid.v4() }; - const callbackFunctionSpy = sinon.spy(callbackFunction); - - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); + sendRequestToAzureMock.callsFake((...args: any[]): {} => { + return args[1](null, resultElement); }); // Act - await serviceClient.getSkus(callbackFunctionSpy); + await method.callback(); // Assert - assertRequestFailed(error, callbackFunctionSpy); - }); - - describe('throws error when response is not success.', () => { - const responseData = [ - [400, defaultResponseBody], - [103, defaultResponseBody], - [530, defaultResponseBody], - [200, incorrectResponseBody]]; - responseData.forEach(async (response) => { - it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, - async () => { - // Arrange - const res = {statusCode: response[0]}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, response[1]); - }); - - // Act - await serviceClient.getSkus(callbackFunctionSpy); - - // Assert - assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); - }); - }); - }); - - it('does not throw error when response is success.', async () => { - // Arrange - const res = {statusCode: 200}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const parsedResult = JSON.parse(defaultResponseBody); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, defaultResponseBody); - }); - - // Act - await serviceClient.getSkus(callbackFunctionSpy); - - // Assert - assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); - }); - }); - - describe('getConsortia', () => { - it('shows error when request failed.', async () => { - // Arrange - const response = sinon.stub(); - const error = { message: uuid.v4() }; - const callbackFunctionSpy = sinon.spy(callbackFunction); - - pipelineMock.callsFake((...args: any[]): {} => { - return args[1](error, response, defaultResponseBody); - }); - - // Act - await serviceClient.getConsortia(callbackFunctionSpy); - - // Assert - assertRequestFailed(error, callbackFunctionSpy); - }); - - describe('throws error when response is not success.', () => { - const responseData = [ - [400, defaultResponseBody], - [103, defaultResponseBody], - [530, defaultResponseBody], - [200, incorrectResponseBody]]; - responseData.forEach(async (response) => { - it(`response status code is ${response[0]} and response body is ${JSON.stringify(response[1])}.`, - async () => { - // Arrange - const res = {statusCode: response[0]}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, response[1]); - }); - - // Act - await serviceClient.getConsortia(callbackFunctionSpy); - - // Assert - assertResponseNotSuccess(callbackFunctionSpy, pipelineCallbackSpy); - }); - }); - }); - - it('does not throw error when response is success.', async () => { - // Arrange - const res = {statusCode: 200}; - const callbackFunctionSpy = sinon.spy(callbackFunction); - const parsedResult = JSON.parse(defaultResponseBody); - let pipelineCallbackSpy: any; - - pipelineMock.callsFake((...args: any[]): {} => { - pipelineCallbackSpy = sinon.spy(args[1]); - return pipelineCallbackSpy(null, res, defaultResponseBody); - }); - - // Act - await serviceClient.getConsortia(callbackFunctionSpy); - - // Assert - assertResponseSuccess(callbackFunctionSpy, pipelineCallbackSpy, parsedResult); + assert.strictEqual(sendRequestToAzureMock.calledOnce, true, 'sendRequestToAzure should called once'); + assert.strictEqual( + callbackFunctionSpy.calledOnceWithExactly(null, resultElement), + true, + 'callbackFunction should called once with correct arguments'); }); }); }); diff --git a/test/TreeManager.test.ts b/test/TreeManager.test.ts index 9588ecd..573d964 100644 --- a/test/TreeManager.test.ts +++ b/test/TreeManager.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import rewire = require('rewire'); describe('TreeManager tests', () => { - const numberOfElements = 3; + const numberOfElements = 4; it(`fillDefaultTypes should return array with ${numberOfElements} elements`, async () => { // Arrange const treeManagerRewire = rewire('../src/services/tree/TreeManager'); diff --git a/test/TruffleCommandsTests/WriteToBuffer.test.ts.orig b/test/TruffleCommandsTests/WriteToBuffer.test.ts.orig deleted file mode 100644 index 12a79ac..0000000 --- a/test/TruffleCommandsTests/WriteToBuffer.test.ts.orig +++ /dev/null @@ -1,72 +0,0 @@ -// 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 * as vscode from 'vscode'; -import { TruffleCommands } from '../../src/commands/TruffleCommands'; -import { Constants } from '../../src/Constants'; - -describe('TruffleCommands', () => { - describe('Integration test', () => { - describe('Success path', () => { - const fileUri = { - fsPath: path.join(__dirname, 'testData', 'TestContract.json'), - } as vscode.Uri; - - const testJson = fs.readFileSync(fileUri.fsPath, null); - const testJsonString = JSON.parse(testJson.toString()); - - it('writeBytecodeToBuffer should write correct bytecode to clipboard', async () => { - // Arrange - const testBytecode = testJsonString[Constants.contractProperties.bytecode]; - - // Act - await TruffleCommands.writeBytecodeToBuffer(fileUri); - - // Assert - assert.strictEqual( - await vscode.env.clipboard.readText(), - testBytecode, - 'clipboard should store correct bytecode'); - }); - - it('writeAbiToBuffer should write correct aby to clipboard', async () => { - // Arrange - const testAbi = JSON.stringify(testJsonString[Constants.contractProperties.abi]); - - // Act - await TruffleCommands.writeAbiToBuffer(fileUri); - - // Assert - assert.strictEqual( - await vscode.env.clipboard.readText(), - testAbi, - 'clipboard should store correct aby'); - }); - }); - - describe('Invalid cases', () => { - const fileUri = { - fsPath: path.join(__dirname, 'WriteToBuffer.test.ts'), - } as vscode.Uri; - - it('writeBytecodeToBuffer throw error when uri is not JSON file', async () => { - // Act and assert - await assert.rejects( - TruffleCommands.writeBytecodeToBuffer(fileUri), - Error, - Constants.errorMessageStrings.InvalidContract); - }); - - it('writeAbiToBuffer throw error when uri is not JSON file', async () => { - // Act and assert - await assert.rejects( - TruffleCommands.writeAbiToBuffer(fileUri), - Error, - Constants.errorMessageStrings.InvalidContract); - }); - }); - }); -}); diff --git a/test/TruffleCommandsTests/deployContracts.test.ts b/test/TruffleCommandsTests/deployContracts.test.ts index fdf4d34..1110256 100644 --- a/test/TruffleCommandsTests/deployContracts.test.ts +++ b/test/TruffleCommandsTests/deployContracts.test.ts @@ -13,12 +13,18 @@ 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 } from '../../src/Models'; +import { CancellationEvent, ItemType } from '../../src/Models'; import { AzureBlockchainNetworkNode, AzureBlockchainProject, AzureBlockchainService, + BlockchainDataManagerNetworkNode, + BlockchainDataManagerProject, + BlockchainDataManagerService, IExtensionItem, + InfuraNetworkNode, + InfuraProject, + InfuraService, LocalNetworkNode, LocalProject, LocalService, @@ -26,7 +32,7 @@ import { Service, } from '../../src/Models/TreeItems'; import { ConsortiumResourceExplorer } from '../../src/resourceExplorers'; -import { GanacheService, MnemonicRepository, TreeManager } from '../../src/services'; +import { GanacheService, MnemonicRepository, OpenZeppelinMigrationsService, TreeManager } from '../../src/services'; import { OpenZeppelinService } from '../../src/services'; import { OZContractValidated } from '../../src/services/openZeppelin/models'; import { TestConstants } from '../TestConstants'; @@ -82,6 +88,8 @@ describe('TruffleCommands', () => { 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); @@ -564,6 +572,28 @@ describe('TruffleCommands', () => { '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); @@ -637,6 +667,8 @@ async function createTestServicesItems(): Promise { const azureBlockchainService = new AzureBlockchainService(); const localService = new LocalService(); + const infuraService = new InfuraService(); + const bdmService = new BlockchainDataManagerService(); const azureBlockchainProject = new AzureBlockchainProject( azureNames.consortium, @@ -657,10 +689,26 @@ async function createTestServicesItems(): Promise { 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); + services.push(azureBlockchainService, localService, infuraService, bdmService); return services; } diff --git a/test/TruffleCommandsTests/deployContracts.test.ts.orig b/test/TruffleCommandsTests/deployContracts.test.ts.orig deleted file mode 100644 index d20d27b..0000000 --- a/test/TruffleCommandsTests/deployContracts.test.ts.orig +++ /dev/null @@ -1,671 +0,0 @@ -// 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 * 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 { TruffleConfiguration } from '../../src/helpers'; -import * as commands from '../../src/helpers/command'; -import * as workspace from '../../src/helpers/workspace'; -import { CancellationEvent } from '../../src/Models'; -import { - AzureBlockchainNetworkNode, - AzureBlockchainProject, - AzureBlockchainService, - IExtensionItem, - LocalNetworkNode, - LocalProject, - LocalService, - Member, - Service, -} from '../../src/Models/TreeItems'; -import { ConsortiumResourceExplorer } from '../../src/resourceExplorers'; -import { GanacheService, MnemonicRepository, TreeManager } from '../../src/services'; -import { OpenZeppelinService, OZContractValidated } from '../../src/services/openZeppelin/OpenZeppelinService'; -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; - let getAllMnemonicPathsMock: sinon.SinonStub; - let saveMnemonicPathMock: sinon.SinonExpectation; - - let writeFileSyncMock: any; - - let getAccessKeysMock: any; - - let getExtensionMock: any; - - let openZeppelinValidateContractsMock: any; - - beforeEach(async () => { - getWorkspaceRootMock = sinon.stub(workspace, '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); - - const openZeppelinServiceMock = sinon.mock(OpenZeppelinService); - openZeppelinValidateContractsMock = openZeppelinServiceMock.expects('validateContracts') - .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', async () => { - // Arrange - checkAppsSilentMock.returns(true); - getWorkspaceRootMock.returns(path.join(__dirname, TestConstants.truffleCommandTestDataFolder)); - executeCommandMock.returns(uuid.v4()); - isHdWalletProviderRequiredMock.returns(false); - - 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, - 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'); - }); - - 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 - openZeppelinValidateContractsMock.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 - openZeppelinValidateContractsMock.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 - openZeppelinValidateContractsMock.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 - openZeppelinValidateContractsMock.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 { - const services: Service[] = []; - - const azureBlockchainService = new AzureBlockchainService(); - const localService = new LocalService(); - - 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); - - azureBlockchainService.addChild(azureBlockchainProject); - localService.addChild(localProject); - - services.push(azureBlockchainService, localService); - - 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('_'); -} diff --git a/test/commands.test.ts b/test/commands.test.ts index c9bb240..f255f64 100644 --- a/test/commands.test.ts +++ b/test/commands.test.ts @@ -171,4 +171,70 @@ describe('Commands helper', () => { return undefined; }); }); + + describe('tryExecuteCommandInFork', () => { + const modulePath = 'some_path'; + const messageData = { command: 'truffleConfig', message: "{ result: 'some message data' }" }; + + let childProcessMock: sinon.SinonMock; + let processMock: cp.ChildProcess; + + beforeEach(() => { + processMock = new events.EventEmitter() as cp.ChildProcess; + processMock.stdout = new events.EventEmitter() as stream.Readable; + processMock.stderr = new events.EventEmitter() as stream.Readable; + processMock.stdin = new stream.Writable(); + childProcessMock = sinon.mock(cp); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('tryExecuteCommandInFork should return correct result', async () => { + // Arrange + const forkMock = childProcessMock.expects('fork').returns(processMock); + + // Act + const commandResultPromise = outputCommandHelper.tryExecuteCommandInFork('workingDirectory', modulePath, ''); + + await new Promise(async (resolve) => { + setTimeout(async () => { + await processMock.emit('message', messageData); + await processMock.emit('exit', 0); + resolve(); + }, 500); + }); + + const commandResult = await commandResultPromise; + + // Assert + assert.strictEqual( + commandResult.messages && commandResult.messages[0], + messageData, + 'commandResult.messages should be equal to test data'); + assert.strictEqual(forkMock.calledOnce, true, 'fork should called once'); + }); + + it('tryExecuteCommandInFork should rejected on error', async () => { + // Arrange + sinon.replace(cp, 'fork', () => { throw new Error(); }); + + // Act + const action = async () => { + return await outputCommandHelper.tryExecuteCommandInFork('workingDirectory', modulePath, ''); + }; + + await new Promise(async (resolve) => { + setTimeout(async () => { + await processMock.emit('message', messageData); + await processMock.emit('exit', 0); + resolve(); + }, 500); + }); + + // Assert + await assert.rejects(action); + }); + }); }); diff --git a/test/commands/OpenZeppelinCommands.test.ts.orig b/test/commands/OpenZeppelinCommands.test.ts.orig deleted file mode 100644 index f0a5e7a..0000000 --- a/test/commands/OpenZeppelinCommands.test.ts.orig +++ /dev/null @@ -1,335 +0,0 @@ -// 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 { TruffleConfiguration } from '../../src/helpers/truffleConfig'; -import * as workspace from '../../src/helpers/workspace'; -import { CancellationEvent } from '../../src/Models'; -import { - IDownloadingResult, - IOZAsset, - IOZContractCategory, - OpenZeppelinMigrationsService, - OpenZeppelinService, - OZAssetType, - PromiseState, -} from '../../src/services'; - -describe('OpenZeppelinCommands tests', () => { - let testCategories: IOZContractCategory[]; - let getCategoriesStub: sinon.SinonStub<[], IOZContractCategory[]>; - let collectAssetsWithDependenciesStub: sinon.SinonStub<[(string[] | undefined)?], IOZAsset[]>; - let downloadFilesStub: sinon.SinonStub<[IOZAsset[], (boolean | undefined)?], Promise>; - let addAssetsToProjectJsonStub: sinon.SinonStub<[IOZAsset[]], Promise>; - let getAssetsStatusStub: sinon.SinonStub; - let generateMigrationsStub: sinon.SinonStub<[IOZAsset[]], Promise>; - let getCategoryApiDocumentationUrlStub: sinon.SinonStub; - - let withProgressStub: sinon.SinonStub<[ProgressOptions, - (progress: Progress, token: CancellationToken) => any], any>; - let showQuickPickStub: sinon.SinonStub< - [QuickPickItem[] | Thenable, (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 openZeppelinCommands: { addCategory: () => Promise }; - - let openStub: sinon.SinonStub; - - beforeEach(() => { - const numberOfCategory = 2; - testCategories = getTestCategories(); - selectedCategory = testCategories[numberOfCategory]; - testAssets = getTestAssetsWithDependencies(selectedCategory.assets); - - const getWorkspaceRootMock = sinon.stub(workspace, 'getWorkspaceRoot'); - getWorkspaceRootMock.returns(path.join(__filename, '../../TruffleCommandsTests/testData')); - - sinon.stub(TruffleConfiguration.TruffleConfig.prototype, 'getConfiguration') - .returns({ contracts_directory: uuid.v4() } as TruffleConfiguration.IConfiguration); - - addAssetsToProjectJsonStub = sinon.stub(OpenZeppelinService, 'addAssetsToProjectJson'); - getCategoriesStub = sinon.stub(OpenZeppelinService, 'getCategories') - .returns(testCategories); - collectAssetsWithDependenciesStub = sinon.stub(OpenZeppelinService, 'collectAssetsWithDependencies') - .returns(testAssets); - getAssetsStatusStub = sinon.stub(OpenZeppelinService, 'getAssetsStatus'); - downloadFilesStub = sinon.stub(OpenZeppelinService, 'downloadFiles'); - generateMigrationsStub = sinon.stub(OpenZeppelinMigrationsService, 'generateMigrations'); - getCategoryApiDocumentationUrlStub = sinon.stub(OpenZeppelinService, 'getCategoryApiDocumentationUrl') - .returns('testUrl'); - - 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, - }); - }); - downloadFilesStub.resolves(testDownloadingResult); - - openStub = sinon.stub().resolves(); - const openZeppelinCommandsRewire = rewire('../../src/commands/OpenZeppelinCommands'); - openZeppelinCommandsRewire.__set__('open', openStub); - openZeppelinCommands = openZeppelinCommandsRewire.__get__('OpenZeppelinCommands'); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('should complete basic pipeline', async () => { - // Arrange - const wereDownloadedMessage = Constants.openZeppelin.wereDownloaded(selectedCategory.assets.length); - showInformationMessageStub.onCall(1).returns(Constants.openZeppelin.moreDetailsButtonTitle); - - // Act - await 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(downloadFilesStub.calledOnce, true, 'downloadFiles should be called'); - assert.strictEqual(addAssetsToProjectJsonStub.called, true, 'addAssetsToProjectJson 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( - getCategoryApiDocumentationUrlStub.calledOnce, - true, - 'getCategoryApiDocumentationUrl should be called', - ); - assert.strictEqual(generateMigrationsStub.called, true, 'generateMigrations should be called'); - }); - - it('should downloads selected category', async () => { - // Act - await openZeppelinCommands.addCategory(); - - // Assert - assert.strictEqual(downloadFilesStub.args[0][0], testAssets, 'downloadFiles should called for selected asset'); - }); - - it('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 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('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 openZeppelinCommands.addCategory(); - - // Assert - assert.strictEqual(showInformationMessageStub.calledThrice, true, 'showQuickPick should called three times'); - assert.strictEqual( - showInformationMessageStub.args[0][0], - Constants.openZeppelin.alreadyExisted(existingAsset), - 'alreadyExisted message should displayed once.', - ); - assert.strictEqual(downloadFilesStub.args[0][1], true, 'downloading should be called with overwrite flag'); - assert.strictEqual(downloadFilesStub.args[0][0].length, testAssets.length, 'downloading full asset of items'); - }); - - it('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 openZeppelinCommands.addCategory(); - - // Assert - assert.strictEqual(showInformationMessageStub.calledThrice, true, 'showQuickPick should called three times.'); - assert.strictEqual( - showInformationMessageStub.args[0][0], - Constants.openZeppelin.alreadyExisted(existingAsset), - 'showQuickPick should called once.', - ); - assert.strictEqual(downloadFilesStub.args[0][1], false, 'downloading should be called without overwrite flag'); - assert.strictEqual(downloadFilesStub.args[0][0].length, missingAsset.length, 'downloading only missing assets'); - }); - - it('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 }, - ]; - downloadFilesStub.resolves([ - { asset: {} as IOZAsset, state: PromiseState.fulfilled }, - ...rejectedAssets, - ]); - showErrorMessageStub - .onCall(0).returns(Constants.openZeppelin.retryButtonTitle) - .onCall(1).returns(Constants.openZeppelin.cancelButtonTitle); - - // Act - await openZeppelinCommands.addCategory(); - - // Assert - assert.strictEqual( - showErrorMessageStub.args[0][0], - Constants.openZeppelin.wereNotDownloaded(rejectedAssets.length), - 'showErrorMessageStub should called with exact message', - ); - assert.strictEqual(downloadFilesStub.calledTwice, true, 'downloadFiles should be retried'); - }); - - it('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 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(downloadFilesStub), true, 'open should be called after downloadFiles'); - }); - - it('should not ask and open category documentation if it doesn\'t exist', async () => { - // Arrange - getCategoryApiDocumentationUrlStub.returns(undefined); - generateMigrationsStub.resolves(); - - // Act - await 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; -} diff --git a/test/commands/ServiceCommands/connectService.integration.test.ts b/test/commands/ServiceCommands/connectService.integration.test.ts index aaae1d4..dea8615 100644 --- a/test/commands/ServiceCommands/connectService.integration.test.ts +++ b/test/commands/ServiceCommands/connectService.integration.test.ts @@ -8,10 +8,11 @@ import * as uuid from 'uuid'; import * as vscode from 'vscode'; import { Constants } from '../../../src/Constants'; import { ItemType } from '../../../src/Models'; -import { AzureBlockchainProject, AzureBlockchainService, Project, Service, ServiceTypes } from '../../../src/Models/TreeItems'; -import { ConsortiumResourceExplorer } from '../../../src/resourceExplorers'; +import { AzureBlockchainProject, AzureBlockchainService, BlockchainDataManagerProject, InfuraProject, Project } from '../../../src/Models/TreeItems'; +import { BlockchainDataManagerResourceExplorer, ConsortiumResourceExplorer, InfuraResourceExplorer } from '../../../src/resourceExplorers'; import { GanacheService, TreeManager } from '../../../src/services'; import { AzureAccountHelper } from '../../testHelpers/AzureAccountHelper'; +const { project } = Constants.treeItemData; describe('Service Commands', () => { let defaultConsortiumName: string; @@ -42,12 +43,12 @@ describe('Service Commands', () => { describe('connectProject returns project', () => { let selectedDestination: any; - let getItemStub: sinon.SinonStub<[ServiceTypes], Service>; let addChildStub: sinon.SinonStub; let showQuickPickMock: sinon.SinonStub; let showInputBoxMock: sinon.SinonExpectation; let selectConsortiumMock: any; let startGanacheServerStub: any; + let selectProjectMock: any; beforeEach(() => { vscodeWindowMock = sinon.mock(vscode.window); @@ -60,7 +61,7 @@ describe('Service Commands', () => { sinon.stub(GanacheService, 'getPortStatus') .resolves(GanacheService.PortStatus.FREE); - getItemStub = sinon.stub(TreeManager, 'getItem') + sinon.stub(TreeManager, 'getItem') .callsFake(() => { const azureBlockchainService = new AzureBlockchainService(); addChildStub = sinon.stub(azureBlockchainService, 'addChild'); @@ -76,13 +77,10 @@ describe('Service Commands', () => { [defaultMemberName], )), ); + sinon.stub(vscode.extensions, 'getExtension').returns(AzureAccountHelper.mockExtension); }); afterEach(() => { - startGanacheServerStub.restore(); - selectConsortiumMock.restore(); - getItemStub.restore(); - vscodeWindowMock.restore(); sinon.restore(); }); @@ -130,7 +128,7 @@ describe('Service Commands', () => { assertAfterEachTest( result, ItemType.LOCAL_PROJECT, - Constants.treeItemData.project.local.contextValue, + project.local.contextValue, expectedLabel, ); assert.strictEqual(showInputBoxMock.called, true, 'showInputBox should be called'); @@ -142,9 +140,6 @@ describe('Service Commands', () => { it('for Azure Blockchain Service destination.', async () => { // Arrange - const getExtensionMock = sinon.stub(vscode.extensions, 'getExtension') - .returns(AzureAccountHelper.mockExtension); - showQuickPickMock.callsFake(async (...args: any[]) => { const destination = args[0].find((x: any) => x.itemType === ItemType.AZURE_BLOCKCHAIN_SERVICE); selectedDestination = destination; @@ -160,16 +155,69 @@ describe('Service Commands', () => { assertAfterEachTest( result, ItemType.AZURE_BLOCKCHAIN_PROJECT, - Constants.treeItemData.project.default.contextValue, + project.azure.contextValue, defaultConsortiumName, ); assert.strictEqual(startGanacheServerStub.notCalled, true, 'startGanacheServer command should not be called'); - assert.strictEqual( - selectConsortiumMock.calledOnce, - true, - 'selectProject should be called once'); + assert.strictEqual(selectConsortiumMock.calledOnce, true, 'selectProject should be called once'); + }); - getExtensionMock.restore(); + it('for Infura Service destination.', async () => { + // Arrange + const label = uuid.v4.toString(); + selectProjectMock = sinon.stub(InfuraResourceExplorer.prototype, 'selectProject'); + const infuraProject = new InfuraProject(label, uuid.v4()); + selectProjectMock.returns(infuraProject); + + showQuickPickMock.callsFake(async (...args: any[]) => { + const destination = args[0].find((x: any) => x.itemType === ItemType.INFURA_SERVICE); + selectedDestination = destination; + selectedDestination.cmd = sinon.spy(destination.cmd); + + return selectedDestination; + }); + + // Act + const result = await serviceCommandsRewire.ServiceCommands.connectProject(); + + // Assert + assertAfterEachTest( + result, + ItemType.INFURA_PROJECT, + project.infura.contextValue, + label, + ); + assert.strictEqual(startGanacheServerStub.notCalled, true, 'startGanacheServer command should not be called'); + assert.strictEqual(selectProjectMock.calledOnce, true, 'selectProject should be called once'); + }); + + it('for Blockchain Data Manager Service destination.', async () => { + // Arrange + const label = uuid.v4.toString(); + selectProjectMock = sinon.stub(BlockchainDataManagerResourceExplorer.prototype, 'selectProject'); + const bdmProject = new BlockchainDataManagerProject(label, uuid.v4(), uuid.v4()); + selectProjectMock.returns(bdmProject); + + showQuickPickMock.callsFake(async (...args: any[]) => { + const destination = args[0].find((x: any) => x.itemType === ItemType.BLOCKCHAIN_DATA_MANAGER_SERVICE); + selectedDestination = destination; + selectedDestination.cmd = sinon.spy(destination.cmd); + + return selectedDestination; + }); + + // Act + const result = await serviceCommandsRewire.ServiceCommands.connectProject(); + + // Assert + assertAfterEachTest( + result, + ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT, + project.bdm.contextValue, + label, + ); + assert.strictEqual(startGanacheServerStub.notCalled, true, 'startGanacheServer command should not be called'); + assert.strictEqual(selectProjectMock.calledOnce, true, 'selectProject should be called once'); }); }); }); diff --git a/test/commands/ServiceCommands/connectService.test.ts b/test/commands/ServiceCommands/connectService.test.ts index bb1197b..e4747ac 100644 --- a/test/commands/ServiceCommands/connectService.test.ts +++ b/test/commands/ServiceCommands/connectService.test.ts @@ -11,12 +11,16 @@ import { ItemType } from '../../../src/Models'; import { AzureBlockchainProject, AzureBlockchainService, + BlockchainDataManagerProject, + BlockchainDataManagerService, IExtensionItem, + InfuraProject, + InfuraService, LocalService, Project, Service, } from '../../../src/Models/TreeItems'; -import { ConsortiumResourceExplorer } from '../../../src/resourceExplorers'; +import { BlockchainDataManagerResourceExplorer, ConsortiumResourceExplorer, InfuraResourceExplorer } from '../../../src/resourceExplorers'; import { GanacheService, TreeManager } from '../../../src/services'; import { AzureAccountHelper } from '../../testHelpers/AzureAccountHelper'; import { getRandomInt } from '../../testHelpers/Random'; @@ -34,13 +38,18 @@ describe('Service Commands', () => { let startGanacheServerMock: sinon.SinonExpectation; let selectConsortiumMock: any; let getExtensionMock: any; + let selectProjectMock: any; let azureGroup: Service; let localGroup: Service; + let infuraGroup: Service; + let bdmGroup: Service; function initializeNetworks() { azureGroup = new AzureBlockchainService(); localGroup = new LocalService(); + infuraGroup = new InfuraService(); + bdmGroup = new BlockchainDataManagerService(); } function createTestServiceItems() { @@ -136,9 +145,58 @@ describe('Service Commands', () => { assertAfterEachTest( result, ItemType.AZURE_BLOCKCHAIN_PROJECT, - project.default.contextValue, + project.azure.contextValue, consortiumName.toString()); }); + + it('for Infura Service destination.', async () => { + // Arrange + const label = uuid.v4.toString(); + getItemMock.returns(infuraGroup); + showQuickPickMock.onCall(0).callsFake((items: any) => { + return items.find((item: any) => item.label === service.infura.label); + }); + + selectProjectMock = sinon.stub(InfuraResourceExplorer.prototype, 'selectProject'); + const infuraProject = new InfuraProject(label, uuid.v4()); + selectProjectMock.returns(infuraProject); + + // Act + const result = await ServiceCommands.connectProject(); + + // Assert + assert.strictEqual(selectProjectMock.calledOnce, true); + assertAfterEachTest( + result, + ItemType.INFURA_PROJECT, + project.infura.contextValue, + label.toString()); + }); + + it('for Blockchain Data Manager Service destination.', async () => { + // Arrange + const label = uuid.v4.toString(); + getItemMock.returns(bdmGroup); + showQuickPickMock.onCall(0).callsFake((items: any) => { + return items.find((item: any) => item.label === service.bdm.label); + }); + + selectProjectMock = sinon.stub(BlockchainDataManagerResourceExplorer.prototype, 'selectProject'); + const bdmProject = new BlockchainDataManagerProject(label, uuid.v4(), uuid.v4()); + selectProjectMock.returns(bdmProject); + + // Act + const result = await ServiceCommands.connectProject(); + + // Assert + assert.strictEqual(getExtensionMock.calledOnce, true); + assert.strictEqual(selectProjectMock.calledOnce, true); + assertAfterEachTest( + result, + ItemType.BLOCKCHAIN_DATA_MANAGER_PROJECT, + project.bdm.contextValue, + label.toString()); + }); }); describe('connectProject should rejects', () => { diff --git a/test/debugAdapter/helper.test.ts.orig b/test/debugAdapter/helper.test.ts.orig deleted file mode 100644 index e1a1780..0000000 --- a/test/debugAdapter/helper.test.ts.orig +++ /dev/null @@ -1,67 +0,0 @@ -import * as assert from 'assert'; -import * as helpers from '../../src/debugAdapter/helpers'; - -describe('helpers unit tests', () => { - it('groupBy should return correct result', () => { - const keyValue1 = '1'; - const keyValue2 = '2'; - const elA = { a: keyValue1, b: 1 }; - const elB = { a: keyValue1, b: 2 }; - const elC = { a: keyValue2, b: 3 }; - const arrayMock = [ elA, elB, elC ]; - const groups = helpers.groupBy(arrayMock, 'a'); - - assert.strictEqual(Object.keys(groups).length, 2, 'groups should be 2'); - assert.strictEqual(Object.keys(groups).includes(keyValue1), true, `groups should contain key ${keyValue1}`); - assert.strictEqual(Object.keys(groups).includes(keyValue2), true, `groups should contain key ${keyValue2}`); - assert.strictEqual(groups[keyValue1][0], elA, 'group 1 should contain elA'); - assert.strictEqual(groups[keyValue1][1], elB, 'group 1 should contain elB'); - assert.strictEqual(groups[keyValue2][0], elC, 'group 2 should contain elC'); - }); - - getTestCasesForSortFilePaths().forEach((testCase) => { - sortFilePathTest(testCase.input, testCase.output); - }); - - function sortFilePathTest(input: string[], output: string[]) { - it('sortFilePaths should return correct result', () => { - const result = helpers.sortFilePaths(input); - output.forEach((element, index) => { - assert.strictEqual(result[index], element, `${index}-th element should equal ${element}`); - }); - }); - } - - function getTestCasesForSortFilePaths() { - return [ - { - input: [ - 'A:\\B\\C\\b.ext', - 'A:/B/C/D/a.ext', - 'A:/B/C/a.ext', - ], - output: [ - 'A:/B/C/a.ext', - 'A:\\B\\C\\b.ext', - 'A:/B/C/D/a.ext', - ], - }, - { - input: [ - 'A:/B/C/E/a.ext', - 'A:/B/C/b.ext', - 'A:/B/C/E/F/a.ext', - 'A:\\B\\C\\a.ext', - 'A:\\B\\C\\D\\A\\B\\b.ext', - ], - output: [ - 'A:\\B\\C\\a.ext', - 'A:/B/C/b.ext', - 'A:\\B\\C\\D\\A\\B\\b.ext', - 'A:/B/C/E/a.ext', - 'A:/B/C/E/F/a.ext', - ], - }, - ]; - } -}); diff --git a/test/openZeppelinHelper.test.ts b/test/openZeppelinHelper.test.ts index 63650dd..de25e88 100644 --- a/test/openZeppelinHelper.test.ts +++ b/test/openZeppelinHelper.test.ts @@ -37,7 +37,7 @@ describe('OpenZeppelinHelper', () => { it('shouldUpgradeOpenZeppelinAsync should return true if current and latest versions differ and confirm dialog is true', async () => { sinon.stub(OpenZeppelinService, 'getCurrentOpenZeppelinVersionAsync').resolves('current'); sinon.stub(OpenZeppelinService, 'getLatestOpenZeppelinVersionAsync').resolves('latest'); - sinon.stub(helpers, 'showConfirmDialogToUpdateOpenZeppelin').resolves(true); + sinon.stub(helpers, 'showConfirmationDialog').resolves(true); const openZeppelinHelperRewire = rewire('../src/helpers/openZeppelinHelper'); diff --git a/test/required.test.ts.orig b/test/required.test.ts.orig deleted file mode 100644 index c3d8995..0000000 --- a/test/required.test.ts.orig +++ /dev/null @@ -1,744 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import * as assert from 'assert'; -import rewire = require('rewire'); -import * as sinon from 'sinon'; -import uuid = require('uuid'); -import * as vscode from 'vscode'; -import { RequiredApps } from '../src/Constants'; -import * as commands from '../src/helpers/command'; -import * as workspace from '../src/helpers/workspace'; -import { TestConstants } from './TestConstants'; - -const nodeValidVersion: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 0, -}; -const npmValidVersion: commands.ICommandResult = { - cmdOutput: '7.0.0', - cmdOutputIncludingStderr: '', - code: 0, -}; -const gitValidVersion: commands.ICommandResult = { - cmdOutput: ' 3.0.0', - cmdOutputIncludingStderr: '', - code: 0, -}; -const pythonValidVersion: commands.ICommandResult = { - cmdOutput: ' 2.9.0', - cmdOutputIncludingStderr: '', - code: 0, -}; -const truffleValidVersion: commands.ICommandResult = { - cmdOutput: 'truffle@5.5.0', - cmdOutputIncludingStderr: '', - code: 0, -}; -const ganacheValidVersion: commands.ICommandResult = { - cmdOutput: 'ganache-cli@6.5.0', - cmdOutputIncludingStderr: '', - code: 0, -}; - -describe('Required helper', () => { - describe('Unit test', () => { - let tryExecuteCommandMock: any; - let getWorkspaceRootMock: any; - let requiredRewire: any; - let showErrorMessageMock: any; - let executeVSCommandMock: any; - let executeCommandMock: any; - - beforeEach(() => { - requiredRewire = rewire('../src/helpers/required'); - tryExecuteCommandMock = sinon.stub(commands, 'tryExecuteCommand'); - getWorkspaceRootMock = sinon.stub(workspace, 'getWorkspaceRoot'); - showErrorMessageMock = sinon.stub(vscode.window, 'showErrorMessage'); - executeVSCommandMock = sinon.stub(vscode.commands, 'executeCommand'); - executeCommandMock = sinon.stub(commands, 'executeCommand'); - }); - - afterEach(() => { - sinon.restore(); - }); - - describe('getNodeVersion', () => { - it('should return empty string when tryExecuteCommand throw an error', async () => { - // Arrange - tryExecuteCommandMock.throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getNodeVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return empty string when tryExecuteCommand return not zero code', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 1, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getNodeVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return version', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getNodeVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - }); - - describe('getNpmVersion', () => { - it('should return empty string when tryExecuteCommand throw an error', async () => { - // Arrange - tryExecuteCommandMock.throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getNpmVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return empty string when tryExecuteCommand return not zero code', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 1, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getNpmVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return version', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getNpmVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - }); - - describe('getGitVersion', () => { - it('should return empty string when tryExecuteCommand throw an error', async () => { - // Arrange - tryExecuteCommandMock.throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getGitVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return empty string when tryExecuteCommand returns not zero code', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: ' 11.0.0', - cmdOutputIncludingStderr: '', - code: 1, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getGitVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return version', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: ' 11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getGitVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - }); - - describe('getPythonVersion', () => { - it('should return empty string when tryExecuteCommand throws an error', async () => { - // Arrange - tryExecuteCommandMock.throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getPythonVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return empty string when tryExecuteCommand returns not zero code', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: ' 11.0.0', - cmdOutputIncludingStderr: '', - code: 1, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getPythonVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - - it('should return version', async () => { - // Arrange - const executionResult: commands.ICommandResult = { - cmdOutput: ' 11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getPythonVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - }); - }); - - describe('getTruffleVersion', () => { - it('should return local version', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult: commands.ICommandResult = { - cmdOutput: 'truffle@11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getTruffleVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should return global version', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult1: commands.ICommandResult = { - cmdOutput: '', - cmdOutputIncludingStderr: '', - code: 0, - }; - - const executionResult2: commands.ICommandResult = { - cmdOutput: 'Truffle v11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(executionResult1); - tryExecuteCommandMock.onCall(1).returns(executionResult2); - - // Act - const result = await requiredRewire.required.getTruffleVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.callCount, 2, 'tryExecuteCommand should be called twice'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should throw an error when tryExecuteCommand throws an error on first call', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - - tryExecuteCommandMock.onCall(0).throws(TestConstants.testError); - - // Act and assert - await assert.rejects(requiredRewire.required.getTruffleVersion(), Error, TestConstants.testError); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should return empty string when tryExecuteCommand throws an error on second call', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult1: commands.ICommandResult = { - cmdOutput: '', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(executionResult1); - tryExecuteCommandMock.onCall(1).throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getTruffleVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.callCount, 2, 'tryExecuteCommand should be called twice'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - }); - - describe('getGanacheVersion', () => { - it('should return local version', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult: commands.ICommandResult = { - cmdOutput: 'ganache-cli@11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - tryExecuteCommandMock.returns(executionResult); - - // Act - const result = await requiredRewire.required.getGanacheVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should return global version', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult1: commands.ICommandResult = { - cmdOutput: '', - cmdOutputIncludingStderr: '', - code: 0, - }; - - const executionResult2: commands.ICommandResult = { - cmdOutput: 'v11.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(executionResult1); - tryExecuteCommandMock.onCall(1).returns(executionResult2); - - // Act - const result = await requiredRewire.required.getGanacheVersion(); - - // Assert - assert.strictEqual(result, '11.0.0', 'returned result should be defined'); - assert.strictEqual(tryExecuteCommandMock.callCount, 2, 'tryExecuteCommand should be called twice'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should throw an error when tryExecuteCommand throw an error on first call', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - - tryExecuteCommandMock.onCall(0).throws(TestConstants.testError); - - // Act and assert - await assert.rejects(requiredRewire.required.getGanacheVersion(), Error, TestConstants.testError); - assert.strictEqual(tryExecuteCommandMock.calledOnce, true, 'tryExecuteCommand should be called once'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - - it('should return empty string when tryExecuteCommand throw an error on second call', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - const executionResult1: commands.ICommandResult = { - cmdOutput: '', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(executionResult1); - tryExecuteCommandMock.onCall(1).throws(TestConstants.testError); - - // Act - const result = await requiredRewire.required.getGanacheVersion(); - - // Assert - assert.strictEqual(result, '', 'returned result should be empty'); - assert.strictEqual(tryExecuteCommandMock.callCount, 2, 'tryExecuteCommand should be called twice'); - assert.strictEqual(getWorkspaceRootMock.calledOnce, true, 'getWorkspaceRoot should be called once'); - }); - }); - - describe('isValid', () => { - const isValidCases = [ - { - maxVersion: undefined, - minVersion: 'v6.0.0', - result: true, - version: 'v11.0.0', - }, - { - maxVersion: 'v12.0.0', - minVersion: 'v6.0.0', - result: true, - version: 'v11.0.0', - }, - { - maxVersion: 'v12.0.0', - minVersion: 'v6.0.0', - result: false, - version: 'v5.0.0', - }, - { - maxVersion: 'v12.0.0', - minVersion: 'v6.0.0', - result: false, - version: 'v13.0.0', - }, - ]; - - isValidCases.forEach((element, index) => { - it(`should return correct result ${index + 1}`, () => { - // Act - const result = requiredRewire.required.isValid(element.version, element.minVersion, element.maxVersion); - - // Assert - assert.strictEqual(result, element.result, 'returned result should be defined'); - }); - }); - }); - - describe('getAllVersions', () => { - it('should return object with applications version', async () => { - // Arrange - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(gitValidVersion); - tryExecuteCommandMock.onCall(3).returns(pythonValidVersion); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.getAllVersions(); - - // Assert - assert.strictEqual(result.length, 6, 'returned result should have length 6'); - assert.strictEqual(tryExecuteCommandMock.callCount, 6, 'tryExecuteCommand should be called 6 times'); - }); - }); - - describe('checkAppsSilent', () => { - it('should return true when all versions are valid', async () => { - // Arrange - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(gitValidVersion); - tryExecuteCommandMock.onCall(3).returns(pythonValidVersion); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkAppsSilent( - RequiredApps.node, - RequiredApps.npm, - RequiredApps.git, - RequiredApps.python, - ); - - // Assert - assert.strictEqual(result, true, 'returned result should be true'); - assert.strictEqual(tryExecuteCommandMock.callCount, 4, 'tryExecuteCommand should be called 4 times'); - }); - - it('should return false when there are invalid versions', async () => { - // Arrange - const executionResultGit: commands.ICommandResult = { - cmdOutput: ' 1.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - const executionResultPython: commands.ICommandResult = { - cmdOutput: ' 2.5.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(executionResultGit); - tryExecuteCommandMock.onCall(3).returns(executionResultPython); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkAppsSilent( - RequiredApps.node, - RequiredApps.npm, - RequiredApps.git, - RequiredApps.python, - ); - - // Assert - assert.strictEqual(result, false, 'returned result should be false'); - assert.strictEqual(tryExecuteCommandMock.callCount, 4, 'tryExecuteCommand should be called 4 times'); - }); - }); - - describe('checkApps', () => { - it('should return true when all versions are valid', async () => { - // Arrange - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(gitValidVersion); - tryExecuteCommandMock.onCall(3).returns(pythonValidVersion); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkApps( - RequiredApps.node, - RequiredApps.npm, - RequiredApps.git, - RequiredApps.python, - ); - - // Assert - assert.strictEqual(result, true, 'returned result should be true'); - assert.strictEqual(tryExecuteCommandMock.callCount, 4, 'tryExecuteCommand should be called 4 times'); - assert.strictEqual(showErrorMessageMock.called, false, 'showErrorMessage shouldn\'t be called'); - assert.strictEqual(executeVSCommandMock.called, false, 'executeVSCommand shouldn\'t be called'); - }); - - it('should return false when there are invalid versions', async () => { - // Arrange - const executionResultGit: commands.ICommandResult = { - cmdOutput: ' 1.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - const executionResultPython: commands.ICommandResult = { - cmdOutput: ' 2.5.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(executionResultGit); - tryExecuteCommandMock.onCall(3).returns(executionResultPython); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkApps( - RequiredApps.node, - RequiredApps.npm, - RequiredApps.git, - RequiredApps.python, - ); - - // Assert - assert.strictEqual(result, false, 'returned result should be false'); - assert.strictEqual(tryExecuteCommandMock.callCount, 4, 'tryExecuteCommand should be called 4 times'); - assert.strictEqual(showErrorMessageMock.called, true, 'showErrorMessage should be called'); - assert.strictEqual(executeVSCommandMock.called, true, 'executeVSCommand should be called'); - }); - }); - - describe('checkRequiredApps', () => { - it('should return true when all versions are valid', async () => { - // Arrange - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(gitValidVersion); - tryExecuteCommandMock.onCall(3).returns(pythonValidVersion); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkRequiredApps(); - - // Assert - assert.strictEqual(result, true, 'returned result should be true'); - assert.strictEqual(tryExecuteCommandMock.callCount, 3, 'tryExecuteCommand should be called 3 times'); - assert.strictEqual(showErrorMessageMock.called, false, 'showErrorMessage shouldn\'t be called'); - assert.strictEqual(executeVSCommandMock.called, false, 'executeVSCommand shouldn\'t be called'); - }); - - it('should return false when there are invalid versions', async () => { - // Arrange - const executionResultGit: commands.ICommandResult = { - cmdOutput: ' 1.0.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - const executionResultPython: commands.ICommandResult = { - cmdOutput: ' 2.5.0', - cmdOutputIncludingStderr: '', - code: 0, - }; - - tryExecuteCommandMock.onCall(0).returns(nodeValidVersion); - tryExecuteCommandMock.onCall(1).returns(npmValidVersion); - tryExecuteCommandMock.onCall(2).returns(executionResultGit); - tryExecuteCommandMock.onCall(3).returns(executionResultPython); - tryExecuteCommandMock.onCall(4).returns(truffleValidVersion); - tryExecuteCommandMock.onCall(5).returns(ganacheValidVersion); - - // Act - const result = await requiredRewire.required.checkRequiredApps(); - - // Assert - assert.strictEqual(result, false, 'returned result should be false'); - assert.strictEqual(tryExecuteCommandMock.callCount, 3, 'tryExecuteCommand should be called 3 times'); - assert.strictEqual(showErrorMessageMock.called, true, 'showErrorMessageMock should be called'); - assert.strictEqual(executeVSCommandMock.called, true, 'executeVSCommand should be called'); - }); - }); - - describe('installNpm', () => { - it('should install npm', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.returns(uuid.v4()); - tryExecuteCommandMock.returns(npmValidVersion); - - // Act - await requiredRewire.required.installNpm(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - - it('should catch errors', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.throws(TestConstants.testError); - tryExecuteCommandMock.returns(npmValidVersion); - - // Act - await requiredRewire.required.installNpm(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - }); - - describe('installTruffle', () => { - it('should install truffle', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.returns(uuid.v4()); - tryExecuteCommandMock.returns(truffleValidVersion); - - // Act - await requiredRewire.required.installTruffle(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - - it('should catch errors', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.throws(TestConstants.testError); - tryExecuteCommandMock.returns(truffleValidVersion); - - // Act - await requiredRewire.required.installTruffle(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - }); - - describe('installGanache', () => { - it('should install ganache-cli', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.returns(uuid.v4()); - tryExecuteCommandMock.returns(ganacheValidVersion); - - // Act - await requiredRewire.required.installGanache(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - - it('should catch errors', async () => { - // Arrange - getWorkspaceRootMock.returns(uuid.v4()); - executeCommandMock.throws(TestConstants.testError); - tryExecuteCommandMock.returns(ganacheValidVersion); - - // Act - await requiredRewire.required.installGanache(); - - // Assert - assert.strictEqual(getWorkspaceRootMock.called, true, 'getWorkspaceRoot should be called'); - assert.strictEqual(executeCommandMock.calledOnce, true, 'executeCommand should be called once'); - assert.strictEqual(tryExecuteCommandMock.called, true, 'tryExecuteCommand should be called'); - }); - }); - }); -}); diff --git a/test/testData/truffleConfigTestdata.ts.orig b/test/testData/truffleConfigTestdata.ts.orig deleted file mode 100644 index d513979..0000000 --- a/test/testData/truffleConfigTestdata.ts.orig +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import * as fs from 'fs-extra'; -import * as path from 'path'; - -export const referenceMnemonic = 'some menmonic some menmonic some menmonic some menmonic some menmonic some menmonic'; - -export const referenceCfgContent = 'const HDWalletProvider = require("truffle-hdwallet-provider");' - + 'module.exports = {' - + ' networks: {' - + ' development: {' - + ' host: "127.0.0.1",' - + ' port: 8545,' - + ' network_id: "*"' - + ' },' - + ' "localhost:123": {' - + ' from: "string",' - + ' gas: 2,' - + ' gasPrice: 3,' - + ' host: "127.0.0.1",' - + ' network_id: "*",' - + ' port: 123,' - + ' provider: new HDWalletProvider(' - + ' fs.readFileSync("path", "encoding"),' - + ' "url"),' - + ' skipDryRun: true,' - + ' timeoutBlocks: 4,' - + ' websockets: false,' - + ' }' - + ' },' - + ' mocha: {},' - + ' compilers: {' - + ' solc: {}' - + ' }' - + '};'; - -export const referenceCfgContentWithDirectories = 'const HDWalletProvider = require("truffle-hdwallet-provider");' - + 'module.exports = {' - + ' contracts_build_directory: "build",' - + ' contracts_directory: "test_contracts",' - + ' migrations_directory: "test_migrations",' - + ' networks: {' - + ' development: {' - + ' host: "127.0.0.1",' - + ' port: 8545,' - + ' network_id: "*"' - + ' },' - + ' "localhost:123": {' - + ' from: "string",' - + ' gas: 2,' - + ' gasPrice: 3,' - + ' host: "127.0.0.1",' - + ' network_id: "*",' - + ' port: 123,' - + ' provider: new HDWalletProvider(' - + ' fs.readFileSync("path", "encoding"),' - + ' "url"),' - + ' skipDryRun: true,' - + ' timeoutBlocks: 4,' - + ' websockets: false,' - + ' }' - + ' },' - + ' mocha: {},' - + ' compilers: {' - + ' solc: {}' - + ' }' - + '};'; - -export const referenceConfiguration = { - contracts_build_directory: 'build\\contracts', - contracts_directory: 'contracts', - migrations_directory: 'migrations', - networks: [ - { - name: 'development', - options: { - host: '127.0.0.1', - network_id: '*', - port: 8545, - }, - }, - { - name: 'localhost:123', - options: { - from: 'string', - gas: 2, - gasPrice: 3, - host: '127.0.0.1', - network_id: '*', - port: 123, - provider: { - mnemonic: referenceMnemonic, - raw: 'new HDWalletProvider(fs.readFileSync(\"path\", \"encoding\"), \"url\")', - url: 'url', - }, - skipDryRun: true, - timeoutBlocks: 4, - websockets: false, - }, - }, - ], -}; - -const referenceAstPath = path.join(__dirname, 'referenceAstObject.json'); -export const referenceAstObject = JSON.parse(fs.readFileSync(referenceAstPath, 'utf-8').toString()); diff --git a/test/truffleConfig.test.ts.orig b/test/truffleConfig.test.ts.orig deleted file mode 100644 index 066c9bb..0000000 --- a/test/truffleConfig.test.ts.orig +++ /dev/null @@ -1,286 +0,0 @@ -// 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 path from 'path'; -import * as sinon from 'sinon'; -import { TruffleConfiguration } from '../src/helpers'; -import * as workspace from '../src/helpers/workspace'; -import * as testData from './testData/truffleConfigTestdata'; - -describe('TruffleConfiguration helper', () => { - - afterEach(() => { - sinon.restore(); - }); - - it('generateMnemonic should return correct sequence', - async () => { - // Act - const result = TruffleConfiguration.generateMnemonic(); - - // Assert - // 11 spaces + 12 words, 1 word = at least 1 char - assert.strictEqual(result.length > 23, true, 'result length should be greater than 23'); - assert.strictEqual(result.split(' ').length, 12, 'number of words should be equal to 12'); - }); - - it('getTruffleConfigUri returns correct value and check path for existence', - async () => { - // Arrange - const referencePath = path.normalize('w:/temp/truffle-config.js'); - sinon.stub(workspace, 'getWorkspaceRoot').returns(path.normalize('w:/temp')); - const pathExistsStub = sinon.stub(fs, 'pathExistsSync').returns(true); - - // Act - const result = TruffleConfiguration.getTruffleConfigUri(); - - // Assert - assert.strictEqual(result, referencePath, 'result should be correct uri'); - assert.strictEqual(pathExistsStub.calledOnce, true, 'pathExists should called once'); - }); - - it('getTruffleConfigUri throw an exception if path is not existed', - async () => { - // Arrange - sinon.stub(workspace, 'getWorkspaceRoot').returns(''); - sinon.stub(fs, 'pathExistsSync').returns(false); - - // Act and Assert - assert.throws(TruffleConfiguration.getTruffleConfigUri); - }); -}); - -describe('class TruffleConfig', () => { - const configPathStub = path.normalize('w:/temp/truffle-config.js'); - let readFileStub: sinon.SinonStub; - let writeFileStub: sinon.SinonStub; - - before(() => { - readFileStub = sinon.stub(fs, 'readFileSync'); - writeFileStub = sinon.stub(fs, 'writeFileSync'); - - readFileStub.withArgs(configPathStub).returns(testData.referenceCfgContent); - readFileStub.withArgs('path').returns(testData.referenceMnemonic); - readFileStub.withArgs('path', 'encoding').returns(testData.referenceMnemonic); - }); - - after(() => { - sinon.restore(); - }); - - afterEach(() => { - sinon.resetHistory(); - }); - - it('getAST should load correct AST', - async () => { - // Arrange - - // Act - const result = (new TruffleConfiguration.TruffleConfig(configPathStub)).getAST(); - - // Assert - assert.deepEqual(result, testData.referenceAstObject, 'result should be equal to expected result'); - assert.strictEqual(readFileStub.callCount > 0, true, 'readFile should called more then 0 times'); - assert.strictEqual( - readFileStub.getCall(0).args[0], - configPathStub, - 'readFile should called with correct arguments'); - }); - - it('writeAST should write ast content to file', - async () => { - // Arrange - - // Act - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - truffleConfig.getAST(); - truffleConfig.writeAST(); - - // Assert - assert.strictEqual(writeFileStub.callCount > 0, true, 'writeFile should called more then 0 times'); - assert.strictEqual( - writeFileStub.getCall(0).args[0], - configPathStub, - 'writeFile should called with correct arguments'); - assert.strictEqual(// 60 - first line, to avoid EOL differences - writeFileStub.getCall(0).args[1].substring(0, 60), - testData.referenceCfgContent.substring(0, 60), - 'writeFile should called with correct arguments', - ); - }); - - it('getNetworks should returns correct networks', - async () => { - // Arrange - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - - // Act - const result = truffleConfig.getNetworks(); - - // Assert - assert.deepStrictEqual( - result[0], - testData.referenceConfiguration.networks[0], - 'result first item should be equal to expected network'); - assert.deepStrictEqual( - result[1], - testData.referenceConfiguration.networks[1], - 'result second item should be equal to expected network'); - }); - - it('setNetworks should add a network item', - async () => { - // Arrange - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - const newNetwork = { - name: 'newNetworkName', - options: { - network_id: 'ntwrk', - }, - }; - - // Act - const resultBefore = truffleConfig.getNetworks(); - truffleConfig.setNetworks(newNetwork); - const resultAfter = truffleConfig.getNetworks(); - - // Assert - assert.strictEqual(resultBefore.length, 2, 'before execution should be 2 networks'); - assert.strictEqual(resultAfter.length, 3, 'after execution should be 2 networks'); - assert.deepStrictEqual(resultAfter[2], newNetwork, 'last network should be equal to test network'); - }); - - it('setNetworks should threw an exception if network already existed', - async () => { - // Arrange - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - const newNetwork = { - name: 'development', - options: { - network_id: 'ntwrk', - }, - }; - const action = () => { - truffleConfig.setNetworks(newNetwork); - }; - - // Act and Assert - assert.throws(action); - }); - - it('importFs should add correct import line', - async () => { - // Arrange - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - - // Act - truffleConfig.importPackage('fs', 'fs'); - - // Assert - const configContent = writeFileStub.getCall(0).args[1]; - assert.strictEqual( - configContent.includes('fs = require(\'fs\');'), - true, - 'configContent should include test string'); - }); -}); - -describe('getConfiguration() in class TruffleConfig', () => { - const configPathStub = path.normalize('w:/temp/truffle-config.js'); - let readFileStub: sinon.SinonStub; - - beforeEach(() => { - readFileStub = sinon.stub(fs, 'readFileSync'); - - readFileStub.withArgs('path').returns(testData.referenceMnemonic); - readFileStub.withArgs('path, encoding').returns(testData.referenceMnemonic); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('getConfiguration returns default configuration', - async () => { - // Arrange - readFileStub.returns(''); - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - - // Act - const result = truffleConfig.getConfiguration(); - - // Assert - assert.strictEqual( - result.contracts_build_directory, - path.normalize('build/contracts'), - 'result should contain contracts build directory'); - assert.strictEqual( - result.contracts_directory, - 'contracts', - 'result should contain contracts directory'); - assert.strictEqual( - result.migrations_directory, - 'migrations', - 'result should contain migration build directory'); - assert.deepStrictEqual(result.networks, undefined, 'result.networks should be undefined'); - }); - - it('getConfiguration returns configuration without required fields', - async () => { - // Arrange - readFileStub.returns(testData.referenceCfgContent); - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - - // Act - const result = truffleConfig.getConfiguration(); - - // Assert - assert.strictEqual( - result.contracts_build_directory, - path.normalize('build/contracts'), - 'result should contain contracts build directory'); - assert.strictEqual( - result.contracts_directory, - 'contracts', - 'result should contain contracts directory'); - assert.strictEqual( - result.migrations_directory, - 'migrations', - 'result should contain migration build directory'); - assert.deepStrictEqual( - result.networks, - testData.referenceConfiguration.networks, - 'result.networks should be equal to test networks'); - }); - - it('getConfiguration returns configuration with required fields', - async () => { - // Arrange - readFileStub.returns(testData.referenceCfgContentWithDirectories); - const truffleConfig = new TruffleConfiguration.TruffleConfig(configPathStub); - - // Act - const result = truffleConfig.getConfiguration(); - - // Assert - assert.strictEqual( - result.contracts_build_directory, - 'build', - 'result should contain contracts build directory'); - assert.strictEqual( - result.contracts_directory, - 'test_contracts', - 'result should contain contracts test_contracts directory'); - assert.strictEqual( - result.migrations_directory, - 'test_migrations', - 'result should contain contracts test_migrations directory'); - assert.deepStrictEqual( - result.networks, - testData.referenceConfiguration.networks, - 'result.networks should be equal to test networks'); - }); -}); diff --git a/test/userInteraction.test.ts.orig b/test/userInteraction.test.ts.orig deleted file mode 100644 index caf5db6..0000000 --- a/test/userInteraction.test.ts.orig +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as sinon from 'sinon'; -import { InputBoxOptions, QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; -import { Constants } from '../src/Constants'; -import * as userInteraction from '../src/helpers/userInteraction'; -import { CancellationEvent } from '../src/Models'; - -interface ITestItems extends QuickPickItem { - id: number; - label: string; - description: string; - testProperty: string; -} - -describe('User interaction test', () => { - let windowMock: sinon.SinonMock; - - beforeEach(() => { - windowMock = sinon.mock(window); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('showInputBox should return a value', async () => { - // Arrange - const option: InputBoxOptions = {}; - - windowMock.expects('showInputBox').withArgs(option).returns('test'); - - // Act - const result = await userInteraction.showInputBox(option); - - // Assert - assert.strictEqual(result, 'test'); - }); - - it('showInputBox should throw cancellation event', async () => { - // Arrange - const option: InputBoxOptions = {}; - - windowMock.expects('showInputBox').withArgs(option).returns(undefined); - - // Act and assert - await assert.rejects(userInteraction.showInputBox(option), CancellationEvent); - }); - - it('showQuickPick should return a value', async () => { - // Arrange - const option: QuickPickOptions = {}; - const items: QuickPickItem[] = [ - { - description: 'test 1', - label: 'test 1', - }, - { - description: 'test 2', - label: 'test 2', - }, - ]; - - windowMock.expects('showQuickPick').withArgs(items, option).returns(items[1]); - - // Act - const result = await userInteraction.showQuickPick(items, option); - - // Assert - assert.deepStrictEqual(result, items[1]); - }); - - it('showQuickPick with custom items should return a value', async () => { - // Arrange - const option: QuickPickOptions = {}; - const items: ITestItems[] = [ - { - description: 'test 1', - id: 1, - label: 'test 1', - testProperty: 'test 1', - }, - { - description: 'test 2', - id: 2, - label: 'test 2', - testProperty: 'test 2', - }, - ]; - - windowMock.expects('showQuickPick').withArgs(items, option).returns(items[1]); - - // Act - const result = await userInteraction.showQuickPick(items, option); - - // Assert - assert.deepStrictEqual(result, items[1]); - }); - - it('showQuickPick should throw cancellation event', async () => { - // Arrange - const option: QuickPickOptions = {}; - const items: QuickPickItem[] = [ - { - description: 'test 1', - label: 'test 1', - }, - { - description: 'test 2', - label: 'test 2', - }, - ]; - - windowMock.expects('showQuickPick').withArgs(items, option).returns(undefined); - - // Act and assert - await assert.rejects(userInteraction.showQuickPick(items, option), CancellationEvent); - }); - - it('showConfirmPaidOperationDialog should throw cancellation event if answer not yes', async () => { - // Arrange - const answer = 'test'; - - windowMock.expects('showInputBox').returns(answer); - - // Act and assert - await assert.rejects(userInteraction.showConfirmPaidOperationDialog(), CancellationEvent); - }); - - it('showConfirmPaidOperationDialog should throw cancellation event if answer undefined', async () => { - // Arrange - windowMock.expects('showInputBox').returns(undefined); - - // Act and assert - await assert.rejects(userInteraction.showConfirmPaidOperationDialog(), CancellationEvent); - }); - - it('showConfirmPaidOperationDialog should not throw cancellation event if answer yes', async () => { - // Arrange - const answer = Constants.confirmationDialogResult.yes; - - windowMock.expects('showInputBox').returns(answer); - - // Act and assert - await userInteraction.showConfirmPaidOperationDialog(); - }); - - it('showOpenFolderDialog should return a folder path', async () => { - // Arrange - const folderPath = 'test/test'; - const uris: Uri[] = [{ fsPath: folderPath} as Uri]; - - windowMock.expects('showOpenDialog').returns(uris); - - // Act - const result = await userInteraction.showOpenFolderDialog(); - - // Assert - assert.deepStrictEqual(result, folderPath); - }); - - it('showOpenFolderDialog should return path of first folder', async () => { - // Arrange - const folderPath1 = 'test/test'; - const folderPath2 = 'test2/test2'; - const uris: Uri[] = [{ fsPath: folderPath1}, { fsPath: folderPath2}] as Uri[]; - - windowMock.expects('showOpenDialog').returns(uris); - - // Act - const result = await userInteraction.showOpenFolderDialog(); - - // Assert - assert.strictEqual(result, folderPath1); - }); - - it('showOpenFolderDialog should throw cancellation event if dialog canceled', async () => { - // Arrange - windowMock.expects('showOpenDialog').returns(undefined); - - // Act and assert - await assert.rejects(userInteraction.showOpenFolderDialog(), CancellationEvent); - }); - - it('saveTextInFile should return file path', async () => { - // Arrange - const fsMock = sinon.mock(fs); - const filePath = 'filePath'; - const text = 'test text'; - - windowMock.expects('showSaveDialog').returns({ fsPath: filePath} as Uri); - fsMock.expects('writeFileSync'); - - // Act - const result = await userInteraction.saveTextInFile(text, filePath); - - // Assert - assert.strictEqual(result, filePath); - }); - - it('saveTextInFile should throw cancellation event if dialog canceled', async () => { - // Arrange - const filePath = 'filePath'; - const text = 'test text'; - - windowMock.expects('showSaveDialog').returns(undefined); - - // Act and assert - await assert.rejects(userInteraction.saveTextInFile(text, filePath), CancellationEvent); - }); -}); diff --git a/tslint.json b/tslint.json index 07158a4..5e39104 100644 --- a/tslint.json +++ b/tslint.json @@ -40,7 +40,8 @@ "linterOptions": { "exclude": [ "node_modules/**", - "src/**/*.d.ts" + "src/**/*.d.ts", + "src/helpers/checkTruffleConfigTemplate.js" ] }, "defaultSeverity": "warning"