diff --git a/packages/composer-playground/.eslintignore b/packages/composer-playground/.eslintignore index 82f5f2e003..f43dbb15f1 100644 --- a/packages/composer-playground/.eslintignore +++ b/packages/composer-playground/.eslintignore @@ -1,4 +1,4 @@ coverage dist node_modules -e2e/data/files +e2e/data diff --git a/packages/composer-playground/.gitignore b/packages/composer-playground/.gitignore index bab02d73e8..7424e9c8f4 100644 --- a/packages/composer-playground/.gitignore +++ b/packages/composer-playground/.gitignore @@ -63,3 +63,6 @@ npm-debug.log # Usabilla code template usabilla.html.template + +#pm2 files +*.pm2 diff --git a/packages/composer-playground/e2e/component/add-file.ts b/packages/composer-playground/e2e/component/add-file.ts index 6a7618b6c7..840abe19d6 100644 --- a/packages/composer-playground/e2e/component/add-file.ts +++ b/packages/composer-playground/e2e/component/add-file.ts @@ -16,7 +16,7 @@ import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; import { dragDropFile } from '../utils/fileUtils'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; export class AddFile { diff --git a/packages/composer-playground/e2e/component/alert.ts b/packages/composer-playground/e2e/component/alert.ts index ab23d9a2a5..6c76ffff77 100644 --- a/packages/composer-playground/e2e/component/alert.ts +++ b/packages/composer-playground/e2e/component/alert.ts @@ -16,7 +16,7 @@ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; export class BusyAlert { diff --git a/packages/composer-playground/e2e/component/deploy.ts b/packages/composer-playground/e2e/component/deploy.ts index eb6c03e2a4..0447106f64 100644 --- a/packages/composer-playground/e2e/component/deploy.ts +++ b/packages/composer-playground/e2e/component/deploy.ts @@ -16,7 +16,7 @@ import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; import { dragDropFile } from '../utils/fileUtils'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; let baseTiles = ['basic-sample-network', 'empty-business-network', 'drag-drop']; @@ -33,10 +33,7 @@ export class Deploy { // Wait for disappear static waitToDisappear(fabric?) { - let wait = Constants.shortWait; - if (fabric) { - wait = null; - } + let wait = fabric ? Constants.vlongwait : Constants.shortWait; return browser.wait(ExpectedConditions.invisibilityOf(element(by.css('.choose-network'))), wait); } @@ -86,7 +83,7 @@ export class Deploy { }) .then(() => { return userSecret.sendKeys(secret); - }) + }); }; static retrieveBaseTileOptions() { diff --git a/packages/composer-playground/e2e/component/editor.ts b/packages/composer-playground/e2e/component/editor.ts index dc66c235b1..d8bbe1ba8b 100644 --- a/packages/composer-playground/e2e/component/editor.ts +++ b/packages/composer-playground/e2e/component/editor.ts @@ -14,7 +14,7 @@ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; import { EditorFile } from './editor-file'; let scrollMe = (target) => { diff --git a/packages/composer-playground/e2e/component/error-alert.ts b/packages/composer-playground/e2e/component/error-alert.ts index 141dd001e5..3e1b4f1da1 100644 --- a/packages/composer-playground/e2e/component/error-alert.ts +++ b/packages/composer-playground/e2e/component/error-alert.ts @@ -16,7 +16,7 @@ import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; import { dragDropFile } from '../utils/fileUtils'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; export class ErrorAlert { diff --git a/packages/composer-playground/e2e/component/import.ts b/packages/composer-playground/e2e/component/import.ts index 3e2e3438b3..f46086ba03 100644 --- a/packages/composer-playground/e2e/component/import.ts +++ b/packages/composer-playground/e2e/component/import.ts @@ -15,7 +15,7 @@ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; import { dragDropFile } from '../utils/fileUtils'; import { OperationsHelper } from '../utils/operations-helper'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; // Initialise known tile orderings let baseTiles = ['basic-sample-network', 'empty-business-network', 'drag-drop']; diff --git a/packages/composer-playground/e2e/component/login.ts b/packages/composer-playground/e2e/component/login.ts index d37e7b232a..7adc8fdc2d 100644 --- a/packages/composer-playground/e2e/component/login.ts +++ b/packages/composer-playground/e2e/component/login.ts @@ -15,7 +15,7 @@ import { browser, element, by, ElementFinder, WebElement } from 'protractor'; import { dragDropFile } from '../utils/fileUtils'; import { ExpectedConditions } from 'protractor'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; import { OperationsHelper } from '../utils/operations-helper'; import { BusyAlert } from './alert'; import * as fs from 'fs'; @@ -48,7 +48,7 @@ export class Login { }); } - // Connect to Playground via named ID Card under named connectino profile + // Connect to Playground via named ID Card under named connection profile static connectViaIdCard(profile: string, networkName: string) { return browser.wait(ExpectedConditions.visibilityOf(element(by.css('.connection-profile'))), Constants.longWait) .then(() => { diff --git a/packages/composer-playground/e2e/component/replace.ts b/packages/composer-playground/e2e/component/replace.ts index 9c1e26f5be..5d6e5bf4ba 100644 --- a/packages/composer-playground/e2e/component/replace.ts +++ b/packages/composer-playground/e2e/component/replace.ts @@ -14,7 +14,7 @@ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; export class Replace { diff --git a/packages/composer-playground/e2e/component/test.ts b/packages/composer-playground/e2e/component/test.ts index c0fd5b60f6..845c2d6e80 100644 --- a/packages/composer-playground/e2e/component/test.ts +++ b/packages/composer-playground/e2e/component/test.ts @@ -14,7 +14,7 @@ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; import { OperationsHelper } from '../utils/operations-helper'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; let scrollMe = (target) => { target.scrollIntoView(true); diff --git a/packages/composer-playground/e2e/constants.ts b/packages/composer-playground/e2e/constants.ts new file mode 100644 index 0000000000..87d2f09de7 --- /dev/null +++ b/packages/composer-playground/e2e/constants.ts @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path = require('path'); + +export class Constants { + + static readonly shortWait = 5000; + + static readonly longWait = 10000; + + static readonly mlongwait = 30000; + + static readonly vlongwait = 240000; + + static readonly webPlaygroundPort = 3001; + static readonly fabricPlaygroundPort = 3002; + + static readonly sampleNetworks = ['basic-sample-network', 'import-network']; + + static readonly downloadLocation = path.join(__dirname, 'downloads'); + static readonly sampleNetworkDir = path.join(__dirname, 'data/sample-networks'); + static readonly tempDir = path.join(__dirname, 'tmp'); + static readonly scriptsDir = path.join(__dirname, 'scripts'); + + static readonly fabricBaseDir = path.join(__dirname, 'fabric'); + static readonly fabricConfigDir = path.join(Constants.fabricBaseDir, 'hlfv1'); + static readonly peerAdminCardName = 'TestPeerAdmin.card'; +} diff --git a/packages/composer-playground/e2e/data/bna/basic-sample-network.bna b/packages/composer-playground/e2e/data/bna/basic-sample-network.bna deleted file mode 100644 index 2404572cb4..0000000000 Binary files a/packages/composer-playground/e2e/data/bna/basic-sample-network.bna and /dev/null differ diff --git a/packages/composer-playground/e2e/data/bna/empty-network.bna b/packages/composer-playground/e2e/data/bna/empty-network.bna deleted file mode 100644 index 77fc79a950..0000000000 Binary files a/packages/composer-playground/e2e/data/bna/empty-network.bna and /dev/null differ diff --git a/packages/composer-playground/e2e/data/bna/importBNA.bna b/packages/composer-playground/e2e/data/bna/importBNA.bna deleted file mode 100644 index f7cef3acaf..0000000000 Binary files a/packages/composer-playground/e2e/data/bna/importBNA.bna and /dev/null differ diff --git a/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/README.md b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/README.md new file mode 100644 index 0000000000..2d9b2af245 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/README.md @@ -0,0 +1,17 @@ +# Welcome to Hyperledger Composer! + +This is the "Hello World" of Hyperledger Composer samples. + +This sample defines a business network composed of a single asset type (`SampleAsset`), a single participant type (`SampleParticipant`), a single transaction type (`SampleTransaction`), and a single event type (`SampleEvent`). + +`SampleAssets` are owned by a `SampleParticipant`, and the value property on a `SampleAsset` can be modified by submitting a `SampleTransaction`. The `SampleTransaction` emits a `SampleEvent` that notifies applications of the old and new values for each modified `SampleAsset`. + +To get started inside Hyperledger Composer you can click the Test tab and create instances of `SampleAsset` and `SampleParticipant`. Make sure that the owner property on the `SampleAsset` refers to a `SampleParticipant` that you have created. + +You can then submit a `SampleTransaction`, making sure that the asset property refers to an asset that you created earlier. After the transaction has been processed you should see that the value property on the asset has been modified, and that a `SampleEvent` has been emitted. + +The logic for updating the asset when a `SampleTransaction` is processed is written in `sample.js`. + +Don't forget that you can import more advanced samples into Hyperledger Composer using the Import/Replace button. + +Have fun learning Hyperledger Composer! \ No newline at end of file diff --git a/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/lib/sample.js b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/lib/sample.js new file mode 100644 index 0000000000..d4595e70a4 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/lib/sample.js @@ -0,0 +1,48 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Sample transaction processor function. + * @param {org.acme.sample.SampleTransaction} tx The sample transaction instance. + * @transaction + */ +function sampleTransaction(tx) { + + // Save the old value of the asset. + var oldValue = tx.asset.value; + + // Update the asset with the new value. + tx.asset.value = tx.newValue; + + // Get the asset registry for the asset. + return getAssetRegistry('org.acme.sample.SampleAsset') + .then(function (assetRegistry) { + + // Update the asset in the asset registry. + return assetRegistry.update(tx.asset); + + }) + .then(function () { + + // Emit an event for the modified asset. + var event = getFactory().newEvent('org.acme.sample', 'SampleEvent'); + event.asset = tx.asset; + event.oldValue = oldValue; + event.newValue = tx.newValue; + emit(event); + + }); + +} + diff --git a/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/models/sample.cto b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/models/sample.cto new file mode 100644 index 0000000000..8358fe1dfe --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/models/sample.cto @@ -0,0 +1,37 @@ +/** + * Sample business network definition. + */ +namespace org.acme.sample + +asset SampleAsset identified by assetId { + o String assetId + --> SampleParticipant owner + o String value +} + +asset SampleComplexAsset identified by assetId { + o String assetId + o Boolean booleanField + o DateTime dateTimeField + o Double doubleField + o Integer integerField + o Long longField + o String[] arrayField optional +} + +participant SampleParticipant identified by participantId { + o String participantId + o String firstName + o String lastName +} + +transaction SampleTransaction { + --> SampleAsset asset + o String newValue +} + +event SampleEvent { + o String oldValue + o String newValue + --> SampleAsset asset +} diff --git a/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/package.json b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/package.json new file mode 100644 index 0000000000..5b05d11886 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/package.json @@ -0,0 +1 @@ +{"name":"basic-sample-network","version":"0.1.0","description":"The Hello World of Hyperledger Composer samples","scripts":{"prepublish":"mkdirp ./dist && composer archive create --sourceType dir --sourceName . -a ./dist/basic-sample-network.bna","pretest":"npm run lint","lint":"eslint .","postlint":"npm run licchk","licchk":"license-check","postlicchk":"npm run doc","doc":"jsdoc --pedantic --recurse -c jsdoc.conf","test-inner":"mocha --recursive && cucumber-js","test-cover":"nyc npm run test-inner","test":"npm run test-inner"},"repository":{"type":"git","url":"https://github.com/hyperledger/composer-sample-networks.git"},"keywords":["sample","network"],"author":"Hyperledger Composer","license":"Apache-2.0","devDependencies":{"browserfs":"^1.2.0","chai":"^3.5.0","chai-as-promised":"^6.0.0","composer-admin":"latest","composer-cli":"latest","composer-client":"latest","composer-connector-embedded":"latest","composer-cucumber-steps":"latest","cucumber":"^2.2.0","eslint":"^3.6.1","istanbul":"^0.4.5","jsdoc":"^3.4.1","license-check":"^1.1.5","mkdirp":"^0.5.1","mocha":"^3.2.0","moment":"^2.17.1","nyc":"^11.0.2"},"license-check-config":{"src":["**/*.js","!./coverage/**/*","!./node_modules/**/*","!./out/**/*","!./scripts/**/*"],"path":"header.txt","blocking":true,"logInfo":false,"logError":true},"nyc":{"exclude":["coverage/**","features/**","out/**","test/**"],"reporter":["text-summary","html"],"all":true,"check-coverage":true,"statements":100,"branches":100,"functions":100,"lines":100}} \ No newline at end of file diff --git a/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/permissions.acl b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/permissions.acl new file mode 100644 index 0000000000..664b2ef409 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/basic-sample-network/permissions.acl @@ -0,0 +1,52 @@ +/** + * Sample access control list. + */ +rule AllAccess { + description: "AllAccess - grant everything to everybody" + participant: "org.hyperledger.composer.system.Participant" + operation: ALL + resource: "org.hyperledger.composer.system.**" + action: ALLOW +} + +rule EverybodyCanReadEverything { + description: "Allow all participants read access to all resources" + participant: "org.acme.sample.SampleParticipant" + operation: READ + resource: "org.acme.sample.*" + action: ALLOW +} + +rule EverybodyCanSubmitTransactions { + description: "Allow all participants to submit transactions" + participant: "org.acme.sample.SampleParticipant" + operation: CREATE + resource: "org.acme.sample.SampleTransaction" + action: ALLOW +} + +rule OwnerHasFullAccessToTheirAssets { + description: "Allow all participants full access to their assets" + participant(p): "org.acme.sample.SampleParticipant" + operation: ALL + resource(r): "org.acme.sample.SampleAsset" + condition: (r.owner.getIdentifier() === p.getIdentifier()) + action: ALLOW +} + +rule NetworkAdminUser { + description: "Grant business network administrators full access to user resources" + participant: "org.hyperledger.composer.system.NetworkAdmin" + operation: ALL + resource: "**" + action: ALLOW +} + +rule NetworkAdminSystem { + description: "Grant business network administrators full access to system resources" + participant: "org.hyperledger.composer.system.NetworkAdmin" + operation: ALL + resource: "org.hyperledger.composer.system.**" + action: ALLOW +} + diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/README.md b/packages/composer-playground/e2e/data/sample-networks/import-network/README.md new file mode 100755 index 0000000000..2d9b2af245 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/README.md @@ -0,0 +1,17 @@ +# Welcome to Hyperledger Composer! + +This is the "Hello World" of Hyperledger Composer samples. + +This sample defines a business network composed of a single asset type (`SampleAsset`), a single participant type (`SampleParticipant`), a single transaction type (`SampleTransaction`), and a single event type (`SampleEvent`). + +`SampleAssets` are owned by a `SampleParticipant`, and the value property on a `SampleAsset` can be modified by submitting a `SampleTransaction`. The `SampleTransaction` emits a `SampleEvent` that notifies applications of the old and new values for each modified `SampleAsset`. + +To get started inside Hyperledger Composer you can click the Test tab and create instances of `SampleAsset` and `SampleParticipant`. Make sure that the owner property on the `SampleAsset` refers to a `SampleParticipant` that you have created. + +You can then submit a `SampleTransaction`, making sure that the asset property refers to an asset that you created earlier. After the transaction has been processed you should see that the value property on the asset has been modified, and that a `SampleEvent` has been emitted. + +The logic for updating the asset when a `SampleTransaction` is processed is written in `sample.js`. + +Don't forget that you can import more advanced samples into Hyperledger Composer using the Import/Replace button. + +Have fun learning Hyperledger Composer! \ No newline at end of file diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/lib/sample.js b/packages/composer-playground/e2e/data/sample-networks/import-network/lib/sample.js new file mode 100755 index 0000000000..18fbf97610 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/lib/sample.js @@ -0,0 +1,47 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Sample transaction processor function. + * @param {org.acme.sample.SampleTransaction} tx The sample transaction instance. + * @transaction + */ +function sampleTransaction(tx) { + + // Save the old value of the asset. + var oldValue = tx.asset.value; + + // Update the asset with the new value. + tx.asset.value = tx.newValue; + + // Get the asset registry for the asset. + return getAssetRegistry('org.acme.sample.SampleAsset') + .then(function (assetRegistry) { + + // Update the asset in the asset registry. + return assetRegistry.update(tx.asset); + + }) + .then(function () { + + // Emit an event for the modified asset. + var event = getFactory().newEvent('org.acme.sample', 'SampleEvent'); + event.asset = tx.asset; + event.oldValue = oldValue; + event.newValue = tx.newValue; + emit(event); + + }); + +} diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/models/sample.cto b/packages/composer-playground/e2e/data/sample-networks/import-network/models/sample.cto new file mode 100755 index 0000000000..bad244aef8 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/models/sample.cto @@ -0,0 +1,27 @@ +/** + * Sample business network definition. + */ +namespace org.acme.sample + +asset SampleAsset identified by assetId { + o String assetId + --> SampleParticipant owner + o String value +} + +participant SampleParticipant identified by participantId { + o String participantId + o String firstName + o String lastName +} + +transaction SampleTransaction { + --> SampleAsset asset + o String newValue +} + +event SampleEvent { + --> SampleAsset asset + o String oldValue + o String newValue +} diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/package.json b/packages/composer-playground/e2e/data/sample-networks/import-network/package.json new file mode 100755 index 0000000000..8e9422b55b --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/package.json @@ -0,0 +1 @@ +{"engines":{"composer":"^0.9.0"},"name":"basic-sample-network","version":"0.1.0","description":"The Hello World of Hyperledger Composer samples","scripts":{"prepublish":"mkdirp ./dist && composer archive create --sourceType dir --sourceName . -a ./dist/basic-sample-network.bna","pretest":"npm run lint","lint":"eslint .","postlint":"npm run licchk","licchk":"license-check","postlicchk":"npm run doc","doc":"jsdoc --pedantic --recurse -c jsdoc.conf","test-inner":"mocha -t 0 --recursive && cucumber-js","test-cover":"nyc npm run test-inner","test":"npm run test-inner"},"repository":{"type":"git","url":"https://github.com/hyperledger/composer-sample-networks.git"},"keywords":["sample","composer","composer-network"],"author":"Hyperledger Composer","license":"Apache-2.0","devDependencies":{"browserfs":"^1.2.0","chai":"^3.5.0","chai-as-promised":"^6.0.0","composer-admin":"^0.9.0","composer-cli":"^0.9.0","composer-client":"^0.9.0","composer-connector-embedded":"^0.9.0","composer-cucumber-steps":"^0.9.0","cucumber":"^2.2.0","eslint":"^3.6.1","istanbul":"^0.4.5","jsdoc":"^3.4.1","license-check":"^1.1.5","mkdirp":"^0.5.1","mocha":"^3.2.0","moment":"^2.17.1","nyc":"^11.0.2"},"license-check-config":{"src":["**/*.js","!./coverage/**/*","!./node_modules/**/*","!./out/**/*","!./scripts/**/*"],"path":"header.txt","blocking":true,"logInfo":false,"logError":true},"nyc":{"exclude":["coverage/**","features/**","out/**","test/**"],"reporter":["text-summary","html"],"all":true,"check-coverage":true,"statements":100,"branches":100,"functions":100,"lines":100}} \ No newline at end of file diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/permissions.acl b/packages/composer-playground/e2e/data/sample-networks/import-network/permissions.acl new file mode 100755 index 0000000000..8496969d23 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/permissions.acl @@ -0,0 +1,27 @@ +/** + * Sample access control list. + */ +rule EverybodyCanReadEverything { + description: "Allow all participants read access to all resources" + participant: "org.acme.sample.SampleParticipant" + operation: READ + resource: "org.acme.sample.*" + action: ALLOW +} + +rule EverybodyCanSubmitTransactions { + description: "Allow all participants to submit transactions" + participant: "org.acme.sample.SampleParticipant" + operation: CREATE + resource: "org.acme.sample.SampleTransaction" + action: ALLOW +} + +rule OwnerHasFullAccessToTheirAssets { + description: "Allow all participants full access to their assets" + participant(p): "org.acme.sample.SampleParticipant" + operation: ALL + resource(r): "org.acme.sample.SampleAsset" + condition: (r.owner.getIdentifier() === p.getIdentifier()) + action: ALLOW +} \ No newline at end of file diff --git a/packages/composer-playground/e2e/data/sample-networks/import-network/queries.qry b/packages/composer-playground/e2e/data/sample-networks/import-network/queries.qry new file mode 100755 index 0000000000..1d8e9dfc01 --- /dev/null +++ b/packages/composer-playground/e2e/data/sample-networks/import-network/queries.qry @@ -0,0 +1,4 @@ +query Q1 { + description: "Select all drivers" + statement: SELECT org.acme.sample.SampleAsset +} diff --git a/packages/composer-playground/e2e/scripts/cleanTestFolders.sh b/packages/composer-playground/e2e/scripts/cleanTestFolders.sh new file mode 100755 index 0000000000..701b154275 --- /dev/null +++ b/packages/composer-playground/e2e/scripts/cleanTestFolders.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Exit on first error, print all commands. +set -ev +set -o pipefail + +# Work from parent (root) e2e directory. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +echo "cleaning folders in ${DIR} and ${HOME}" +rm -rf ${DIR}/downloads +rm -rf ${DIR}/tmp/*.bna +rm -rf ${DIR}/verdaccio/storage/* +rm -rf ${DIR}/fabric/cards/*.card +rm -rf ${HOME}/.composer/cards/Test* +rm -rf ${HOME}/.composer/client-data/Test* +rm -rf ${HOME}/.composer/cards/admin* +rm -rf ${HOME}/.composer/client-data/admin* diff --git a/packages/composer-playground/e2e/scripts/cleanupFabric.sh b/packages/composer-playground/e2e/scripts/cleanupFabric.sh new file mode 100755 index 0000000000..2e8e5a4120 --- /dev/null +++ b/packages/composer-playground/e2e/scripts/cleanupFabric.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Exit on first error, print all commands. +set -ev +set -o pipefail + +# Grab the parent (root) directory. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" + +DOCKER_FILE=${DIR}/fabric/hlfv1/docker-compose.yml + +ARCH=$ARCH docker-compose -f ${DOCKER_FILE} kill +ARCH=$ARCH docker-compose -f ${DOCKER_FILE} down diff --git a/packages/composer-playground/e2e/scripts/configureGateway.sh b/packages/composer-playground/e2e/scripts/configureGateway.sh new file mode 100755 index 0000000000..45020d0924 --- /dev/null +++ b/packages/composer-playground/e2e/scripts/configureGateway.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Exit on first error, print all commands. +set -ev +set -o pipefail + +# Called by package.json start-verdaccio +# Need to add a '.npmrc' file into composer-runtime-hlfv1 BEFORE PUBLISHING +# so that the runtime containers can use it to retrieve from verdaccio + +cd ../composer-runtime-hlfv1 +echo "Setting npmrc config file in: " && pwd; + +if [ `uname` = "Darwin" ]; then + GATEWAY=docker.for.mac.localhost +else + GATEWAY="$(docker inspect hlfv1_default | grep Gateway | cut -d \" -f4)" +fi +echo "Setting gateway http://${GATEWAY}:4874" +echo registry=http://${GATEWAY}:4874 > .npmrc +echo fetch-retries=10 >> .npmrc + +# Verdaccio server requires a dummy user if publishing via npm +touch ${HOME}/.npmrc +echo '//localhost:4874/:_authToken="foo"' > ${HOME}/.npmrc +echo fetch-retries=10 >> ${HOME}/.npmrc diff --git a/packages/composer-playground/e2e/fabric/scripts/setupFabric.sh b/packages/composer-playground/e2e/scripts/setupFabric.sh similarity index 84% rename from packages/composer-playground/e2e/fabric/scripts/setupFabric.sh rename to packages/composer-playground/e2e/scripts/setupFabric.sh index 14fc0f944c..b686b5285c 100755 --- a/packages/composer-playground/e2e/fabric/scripts/setupFabric.sh +++ b/packages/composer-playground/e2e/scripts/setupFabric.sh @@ -1,9 +1,13 @@ -set -e +#!/bin/bash + +# Exit on first error, print all commands. +set -ev +set -o pipefail # Set ARCH ARCH=`uname -m` -# Grab the parent (root) directory. +# Grab the fabric directory. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" rm -rf ${HOME}/.composer/cards/Test* @@ -18,7 +22,7 @@ export COMPOSER_PORT_WAIT_SECS=30 export COMPOSER_DEPLOY_WAIT_SECS=500 export COMPOSER_TIMEOUT_SECS=500 -DOCKER_FILE=${DIR}/hlfv1/docker-compose.yml +DOCKER_FILE=${DIR}/fabric/hlfv1/docker-compose.yml docker pull hyperledger/fabric-peer:$ARCH-1.1.0-rc1 docker pull hyperledger/fabric-ca:$ARCH-1.1.0-rc1 @@ -29,7 +33,9 @@ docker pull hyperledger/fabric-couchdb:$ARCH-0.4.6 if [ -d ./hlfv1/crypto-config ]; then rm -rf ./hlfv1/crypto-config fi -cd hlfv1 + +cd "${DIR}"/fabric/hlfv1 + tar -xvf crypto-config.tar.gz for KEY in $(find crypto-config -type f -name "*_sk"); do @@ -37,18 +43,11 @@ for KEY in $(find crypto-config -type f -name "*_sk"); do mv ${KEY} ${KEY_DIR}/key.pem done +echo Using docker file ${DOCKER_FILE} ARCH=$ARCH docker-compose -f ${DOCKER_FILE} kill ARCH=$ARCH docker-compose -f ${DOCKER_FILE} down ARCH=$ARCH docker-compose -f ${DOCKER_FILE} up -d -if [ `uname` = "Darwin" ]; then - GATEWAY=docker.for.mac.localhost -else - GATEWAY="$(docker inspect hlfv1_default | grep Gateway | cut -d \" -f4)" -fi -echo registry=http://${GATEWAY}:4873 > .npmrc -echo fetch-retries=10 >> .npmrc - # Create the channel docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c composerchannel -f /etc/hyperledger/configtx/composer-channel.tx # Join peer0 from org1 to the channel. @@ -56,4 +55,4 @@ docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.ex # Fetch the channel. docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel fetch config -o orderer.example.com:7050 -c composerchannel composerchannel.block # Join peer0 from org2 to the channel. -docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel join -b composerchannel.block \ No newline at end of file +docker exec -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org2.example.com/msp" peer0.org2.example.com peer channel join -b composerchannel.block diff --git a/packages/composer-playground/e2e/specs/editor-define.spec.ts b/packages/composer-playground/e2e/specs/editor-define.spec.ts index 7414b09559..daa70a9e35 100644 --- a/packages/composer-playground/e2e/specs/editor-define.spec.ts +++ b/packages/composer-playground/e2e/specs/editor-define.spec.ts @@ -11,22 +11,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { browser, element, by } from 'protractor'; -import { ExpectedConditions } from 'protractor'; -import { OperationsHelper } from '../utils/operations-helper'; -import { Editor } from '../component/editor'; -import { Import } from '../component/import'; -import { Login } from '../component/login'; -import { Replace } from '../component/replace'; +import { browser, by } from 'protractor'; import { AddFile } from '../component/add-file'; +import { Editor } from '../component/editor'; import { EditorFile } from '../component/editor-file'; import { ErrorAlert } from '../component/error-alert'; -import { dragDropFile, waitForFileToExist, retrieveZipContentList } from '../utils/fileUtils'; +import { Import } from '../component/import'; +import { OperationsHelper } from '../utils/operations-helper'; +import { Replace } from '../component/replace'; +import { waitForFileToExist, retrieveZipContentList } from '../utils/fileUtils'; -import * as chai from 'chai'; import * as fs from 'fs'; import * as JSZip from 'jszip'; - +import * as chai from 'chai'; let expect = chai.expect; describe('Editor Define', (() => { diff --git a/packages/composer-playground/e2e/specs/playground-tutorial.spec.ts b/packages/composer-playground/e2e/specs/playground-tutorial.spec.ts index decadcc392..23bfdf946a 100644 --- a/packages/composer-playground/e2e/specs/playground-tutorial.spec.ts +++ b/packages/composer-playground/e2e/specs/playground-tutorial.spec.ts @@ -12,26 +12,20 @@ * limitations under the License. */ -import { browser, element, by } from 'protractor'; -import { ExpectedConditions } from 'protractor'; -import { OperationsHelper } from '../utils/operations-helper'; -import { Editor } from '../component/editor'; -import { Test } from '../component/test'; -import { Import } from '../component/import'; -import { Deploy } from '../component/deploy'; -import { Login } from '../component/login'; -import { Replace } from '../component/replace'; +import { ExpectedConditions, browser, element, by } from 'protractor'; + import { AddFile } from '../component/add-file'; -import { EditorFile } from '../component/editor-file'; -import { ErrorAlert } from '../component/error-alert'; -import { dragDropFile, waitForFileToExist, retrieveZipContentList } from '../utils/fileUtils'; import { BusyAlert } from '../component/alert'; -import { Constants } from '../utils/constants'; +import { Constants } from '../constants'; +import { Deploy } from '../component/deploy'; +import { Editor } from '../component/editor'; +import { EditorFile } from '../component/editor-file'; +import { Login } from '../component/login'; +import { OperationsHelper } from '../utils/operations-helper'; +import { Test } from '../component/test'; -import * as chai from 'chai'; import * as fs from 'fs'; -import * as JSZip from 'jszip'; - +import * as chai from 'chai'; let expect = chai.expect; describe('Playground Tutorial Define', (() => { diff --git a/packages/composer-playground/e2e/specs/round-trip.spec.ts b/packages/composer-playground/e2e/specs/round-trip.spec.ts index b839045ebe..f25c77458f 100644 --- a/packages/composer-playground/e2e/specs/round-trip.spec.ts +++ b/packages/composer-playground/e2e/specs/round-trip.spec.ts @@ -12,16 +12,16 @@ * limitations under the License. */ +import { browser } from 'protractor'; import { Login } from '../component/login'; import { OperationsHelper } from '../utils/operations-helper'; -import { browser, element, by, ElementFinder, WebElement } from 'protractor'; -import * as fs from 'fs'; -import { Deploy } from '../component/deploy'; import { BusyAlert, SuccessAlert } from '../component/alert'; -import * as child from 'child_process'; +import { Constants } from '../constants'; import { CliHelper } from '../utils/cli-helper'; -import * as chai from 'chai'; +import { Deploy } from '../component/deploy'; +import * as fs from 'fs'; +import * as chai from 'chai'; let expect = chai.expect; describe('Fabric round trip', (() => { @@ -37,14 +37,15 @@ describe('Fabric round trip', (() => { }); it('should add the TestPeerAdmin card', () => { - return Login.importBusinessNetworkCard('/tmp/TestPeerAdmin.card') + return Login.importBusinessNetworkCard(Constants.tempDir + '/' + Constants.peerAdminCardName) .then(() => { return SuccessAlert.waitToDisappear(); }); }); it('should be able to deploy a business network against TestPeerAdmin', () => { - return Login.deployNewToProfile(JSON.parse(fs.readFileSync(__dirname + '/../fabric/hlfv1/profiles/basic-connection-org1.json').toString()).name) + let connectionProfile = JSON.parse(fs.readFileSync(Constants.fabricConfigDir + '/profiles/basic-connection-org1.json').toString()); + return Login.deployNewToProfile(connectionProfile.name) .then(() => { return Deploy.waitToAppear(); }) @@ -77,15 +78,19 @@ describe('Fabric round trip', (() => { it('should be able to import the downloaded card into composer-cli and ping', () => { const cardName = 'adminCLI@fabric-business-network'; - return CliHelper.importCard(__dirname + '/../downloads/admin.card', cardName) - .then((out) => { - expect(out).to.include(`Card name: ${cardName}`); - expect(out).to.include('Command succeeded'); + const cardPath = Constants.downloadLocation + '/admin.card'; + + return CliHelper.importCard(cardPath, cardName) + .then((repsonse) => { + expect(repsonse).to.include(`Card name: ${cardName}`); + expect(repsonse).to.include('Command succeeded'); + }) + .then(() => { return CliHelper.pingCard(cardName); }) - .then((out) => { - expect(out).to.include('participant: org.hyperledger.composer.system.NetworkAdmin#admin'); - expect(out).to.include('Command succeeded'); + .then((repsonse) => { + expect(repsonse).to.include('participant: org.hyperledger.composer.system.NetworkAdmin#admin'); + expect(repsonse).to.include('Command succeeded'); }); }); })); diff --git a/packages/composer-playground/e2e/specs/welcome.spec.ts b/packages/composer-playground/e2e/specs/welcome.spec.ts index bb868caf6a..323cecdd1b 100644 --- a/packages/composer-playground/e2e/specs/welcome.spec.ts +++ b/packages/composer-playground/e2e/specs/welcome.spec.ts @@ -13,10 +13,9 @@ */ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; +import { Constants } from '../constants'; import { OperationsHelper } from '../utils/operations-helper'; -import { Constants } from '../utils/constants'; - describe('Welcome Splash', (() => { beforeAll(() => { diff --git a/packages/composer-playground/e2e/test.js b/packages/composer-playground/e2e/test.js deleted file mode 100644 index 37e65442f1..0000000000 --- a/packages/composer-playground/e2e/test.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -'use strict'; - -/* eslint-disable no-control-regex */ -/* eslint-disable no-console */ -/* eslint-disable no-use-before-define */ - -const exec = require('child_process').exec; -const spawn = require('child_process').spawn; -const fabric = require(__dirname+'/utils/fabric-helper'); - -// Note that this script is called via npm run e2e:main/nobuild and consequently all -// paths are relative to that calling location (~/composer-playground) - -// Are we running against fabric? -const args = process.argv.slice(process.execArgv.length + 2); -if (args.includes('--fabric') || args.includes('-f')) { - runFabric(); -} -else { - runTests('web'); -} - -async function runFabric() { // eslint-disable-line - console.log('Setting up fabric, one moment...'); - await fabric.start(); - console.log('Fabric setup complete'); - - console.log('Creating peer admin card'); - await fabric.createPeerAdmin(); - console.log('Card created'); - - runTests('fabric'); -} - -function runTests(connection) { // eslint-disable-line - // Start the target test server as a spawned child process in test mode (no npm connections) - let childServer = spawn('node', ['cli.js', '-p', '3001', '-test']); - - // Execute protractor and attach to listeners - let childProtractor = exec(`webdriver-manager update --gecko false && protractor -- protractor.${connection}.conf.js`); - // Log all output of Protractor run - childProtractor.stdout.on('data', function(data) { - // do not log return characters or 'green dot progress' - let msg = data.replace(/\n$/, ''); - msg = msg.replace(/\x1b\[32m.\x1b\[0m/, ''); - if (msg.length) { - console.log(msg); - } - }); - // Log ony error output - childProtractor.stderr.on('data', function(data) { - console.log('stdErr: ' + data); - }); - // Capture Protactor return code - childProtractor.on('close', async function(code) { - console.log('Protractor return code: ', code); - if(code !== 0) { - code = 1; - } - console.log('Exit return code: ', code); - childServer.kill(); - if (connection === 'fabric') { - console.log('Cleaning up fabric, one moment\n'); - await fabric.stop(); - console.log('Cleanup complete. Exiting'); - } - process.exit(code); - }); -} diff --git a/packages/composer-playground/e2e/test.ts b/packages/composer-playground/e2e/test.ts new file mode 100644 index 0000000000..b8d4e25343 --- /dev/null +++ b/packages/composer-playground/e2e/test.ts @@ -0,0 +1,110 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { exec, spawn } from 'child_process'; +import path = require('path'); +import fs = require('fs'); + +import { FabricHelper } from './utils/fabric-helper'; +import { CodeRunner } from './utils/code-runner'; +import { Constants } from './constants'; +import { ComposerUtils } from './utils/composer-utils'; + +// Build test BNAs from sample networks and then proceed with test +let networks = Constants.sampleNetworks; +networks.reduce((chainPromise, network) => { + return chainPromise.then(() => { + let networkPath = path.join(Constants.sampleNetworkDir, network); + let bnaFile = path.join(Constants.tempDir, network) + '.bna'; + + if (fs.existsSync(bnaFile)) { + return Promise.resolve(); + } else { + return ComposerUtils.buildArchive(networkPath, bnaFile); + } + }); +}, Promise.resolve()) +.then(() => { + // Are we running against fabric? + const args = process.argv; + if (args.indexOf('--fabric') !== -1 || args.indexOf('-f') !== -1) { + return runFabric(); + } else { + return runTests('web'); + } +}) +.catch((err) => { + console.log('Error in test runner: ', err); + process.exit(1); +}); + +function runFabric() { + console.log('Setting up fabric, one moment...'); + return FabricHelper.createPeerAdmin() + .then (() => { + return runTests('fabric'); + }); +} + +function runTests(connection) { + let childServer; + // Start the target test server as a spawned child process in test mode (no npm connections) + if (connection.localeCompare('web') === 0) { + // Standard test, use cli.js + console.log('Targetting local build Playground with web connector'); + childServer = spawn('node', ['cli.js', '-p', Constants.webPlaygroundPort.toString(), '-test']); + } else if (connection.localeCompare('fabric') === 0) { + // Integration test, use npm module + console.log('Targetting npm Playground with fabric connector'); + childServer = spawn('composer-playground', ['-p', Constants.fabricPlaygroundPort.toString(), '-test']); + } else { + console.log('Unspecified or invalid parameter passed to runTests() method ' + connection + '] exiting.'); + process.exit(1); + } + + // Execute protractor and attach to listeners + let childProtractor = exec(`webdriver-manager update --gecko false && protractor -- protractor.${connection}.conf.js`); + // Log all output of Protractor run + childProtractor.stdout.on('data', (data) => { + // do not log return characters or 'green dot progress' + let msg = data.toString(); + msg.replace(/\n$/, ''); + msg = msg.replace(/\x1b\[32m.\x1b\[0m/, ''); + if (msg.length) { + console.log(msg); + } + }); + // Log ony error output + childProtractor.stderr.on('data', (data) => { + console.log('stdErr: ' + data); + }); + // Capture Protactor return code + childProtractor.on('close', (code) => { + console.log('Protractor return code: ', code); + if (code !== 0) { + code = 1; + } + console.log('Exit return code: ', code); + childServer.kill(); + if (connection === 'fabric') { + console.log('Cleaning up fabric, one moment\n'); + return FabricHelper.stop() + .then(() => { + process.exit(code); + }); + } else { + process.exit(code); + } + }); +} diff --git a/packages/composer-playground/e2e/tmp/.gitignore b/packages/composer-playground/e2e/tmp/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/packages/composer-playground/e2e/tmp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/packages/composer-playground/e2e/utils/cli-helper.ts b/packages/composer-playground/e2e/utils/cli-helper.ts index cc16717a71..2f493b511a 100644 --- a/packages/composer-playground/e2e/utils/cli-helper.ts +++ b/packages/composer-playground/e2e/utils/cli-helper.ts @@ -12,17 +12,15 @@ * limitations under the License. */ -import * as path from 'path'; -import { Promise } from 'bluebird'; -import { exec } from 'child_process'; -const pExec = Promise.promisify(exec); +import { CodeRunner } from './code-runner'; export class CliHelper { static importCard(filePath: string, cardName: string) { - return pExec(`composer card import --file ${filePath} --name ${cardName}`); + return CodeRunner.runCode(`composer card import --file ${filePath} --name ${cardName}`); } static pingCard(cardName: string) { - return pExec(`composer network ping --card ${cardName}`); + return CodeRunner.runCode(`composer network ping --card ${cardName}`); } + } diff --git a/packages/composer-playground/e2e/utils/code-runner.ts b/packages/composer-playground/e2e/utils/code-runner.ts new file mode 100644 index 0000000000..f1dd05b36b --- /dev/null +++ b/packages/composer-playground/e2e/utils/code-runner.ts @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { exec } from 'child_process'; +import stripAnsi = require('strip-ansi'); + +export class CodeRunner { + + /** + * Command to run a command line command + * @param {String} cmd - command with parameters to be run + * @return {Promise} - Promise that will be resolved or rejected with an error + */ + static runCode(cmd): Promise { + if (typeof cmd !== 'string') { + return Promise.reject('Command passed to function was not a string'); + } else { + console.log('Running command: ', cmd); + + let stdout; + let stderr; + + return new Promise((resolve, reject) => { + let childCliProcess = exec(cmd); + + childCliProcess.stdout.setEncoding('utf8'); + childCliProcess.stderr.setEncoding('utf8'); + + childCliProcess.stdout.on('data', (data) => { + data = stripAnsi(data); + console.log('STDOUT', data); + stdout += data; + }); + + childCliProcess.stderr.on('data', (data) => { + data = stripAnsi(data); + console.log('STDERR', data); + stderr += data; + }); + + childCliProcess.on('error', (error) => { + error = stripAnsi(error); + console.log('ERR', error); + reject({ error: error, stdout: stdout, stderr: stderr }); + }); + + childCliProcess.on('close', (code) => { + if (code && code !== 0 ) { + reject({ stdout: stdout, stderr: stderr }); + } else { + resolve(stdout); + } + }); + }); + } + } +} diff --git a/packages/composer-playground/e2e/utils/composer-utils.ts b/packages/composer-playground/e2e/utils/composer-utils.ts new file mode 100644 index 0000000000..0fd5918149 --- /dev/null +++ b/packages/composer-playground/e2e/utils/composer-utils.ts @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Constants } from '../constants'; +import { BusinessNetworkDefinition } from 'composer-admin'; +import fs = require('fs'); + +/** Class to help with Composer resources */ +export class ComposerUtils { + + /** + * Build a BNA file from a resource directory + * @param sourceDirectory {string} path to the resources for the business network + * @param outFile the output BNA file + */ + static buildArchive(sourceDirectory, outFile) { + return BusinessNetworkDefinition.fromDirectory(sourceDirectory).then((defn) => { + // need to write this out to the required file now. + return defn.toArchive() + .then((archive) => { + // write the buffer to a file + fs.writeFileSync(outFile, archive); + return; + }); + }); + } +} diff --git a/packages/composer-playground/e2e/utils/constants.ts b/packages/composer-playground/e2e/utils/constants.ts deleted file mode 100644 index 3eb83d9f8c..0000000000 --- a/packages/composer-playground/e2e/utils/constants.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export class Constants { - - static readonly shortWait = 5000; - - static readonly longWait = 10000; - - static readonly mlongwait = 30000; - - static readonly vlongwait = 60000; - -} diff --git a/packages/composer-playground/e2e/utils/fabric-helper.js b/packages/composer-playground/e2e/utils/fabric-helper.js deleted file mode 100644 index 62f87c51db..0000000000 --- a/packages/composer-playground/e2e/utils/fabric-helper.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const Promise = require('bluebird'); -const exec = Promise.promisify(require('child_process').exec); -const fs = require('fs'); - -/** Class to help with starting and clearing fabric */ -class FabricHelper { - /** Runs script to setup the fabric environment - * @returns {string} - output from running setupFabric.sh - */ - static start() { - return exec(__dirname + '/../fabric/scripts/setupFabric.sh'); - } - - /** Runs script to cleanup the fabric environment - * @returns {string} - output from running cleanupFabric.sh - */ - static stop() { - return exec(__dirname + '/../fabric/scripts/cleanupFabric.sh'); - } - - /** Generates a peer admin card to use - * @returns {string} - name of card created - */ - static async createPeerAdmin() { - - const cardName = 'TestPeerAdmin'; - const cp = __dirname + '/../fabric/hlfv1/profiles/basic-connection-org1.json'; - const pub = __dirname + '/../fabric/hlfv1/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem'; - const priv = __dirname + '/../fabric/hlfv1/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/key.pem'; - - // check we have the certificates - if (!fs.existsSync(pub) || !fs.existsSync(priv)) { - throw new Error('Certificate(s) not found'); - } - - // check we have the connection proile - if (!fs.existsSync(cp)) { - throw new Error('Connection profile not found'); - } - - await exec(`composer card create -p ${__dirname}/../fabric/hlfv1/profiles/basic-connection-org1.json -u TestPeerAdmin -r PeerAdmin -r ChannelAdmin -f /tmp/${cardName}.card -c ${pub} -k ${priv}`); - - return `/tmp/${cardName}.card`; - } -} -module.exports = FabricHelper; \ No newline at end of file diff --git a/packages/composer-playground/e2e/utils/fabric-helper.ts b/packages/composer-playground/e2e/utils/fabric-helper.ts new file mode 100644 index 0000000000..97616e0c99 --- /dev/null +++ b/packages/composer-playground/e2e/utils/fabric-helper.ts @@ -0,0 +1,75 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Constants } from '../constants'; +import { CodeRunner } from './code-runner'; +import fs = require('fs'); +import path = require('path'); + +/** Class to help with starting and clearing fabric */ +export class FabricHelper { + + /** + * Runs script to cleanup the fabric environment + * @returns {string} - output from running cleanupFabric.sh + */ + static stop() { + return CodeRunner.runCode(path.join(Constants.scriptsDir, 'cleanTestFolders.sh')) + .then(() => { + return CodeRunner.runCode(path.join(Constants.scriptsDir, 'cleanupFabric.sh')); + }); + } + + /** + * Generates a peer admin card to use + */ + static createPeerAdmin() { + + const cp = path.join(Constants.fabricConfigDir, 'profiles/basic-connection-org1.json'); + const pub = path.join(Constants.fabricConfigDir, 'crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem'); + const priv = path.join(Constants.fabricConfigDir, 'crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/key.pem'); + const outputFile = path.join(Constants.tempDir, Constants.peerAdminCardName); + // check we have the certificates + if (!fs.existsSync(pub)) { + throw new Error('Public Certificate not found at location: ' + pub); + } + + if (!fs.existsSync(priv)) { + throw new Error('Private certificate not found at location: ' + priv); + } + + // check we have the connection proile + if (!fs.existsSync(cp)) { + throw new Error('Connection profile not found at location: ' + cp); + } + + return CodeRunner.runCode(`composer card create -p ${cp} -u TestPeerAdmin -r PeerAdmin -r ChannelAdmin -f ${outputFile} -c ${pub} -k ${priv}`); + } + + /** + * Build and deploy a sample business network + * @param {String} name The name of the business network to deploy + * @return {Promise} + */ + static async deploySampleNetwork(name) { + const sampleDir = path.join(Constants.sampleNetworkDir, name); + const bnaFile = path.join(Constants.tempDir, name) + '.bna'; + const card = path.join(Constants.tempDir, 'networkadmin') + '.card'; + + await CodeRunner.runCode(`composer runtime install --card TestPeerAdmin@org1 --businessNetworkName ${name}`); + await CodeRunner.runCode(`composer runtime install --card TestPeerAdmin@org2 --businessNetworkName ${name}`); + await CodeRunner.runCode(`composer network start --card TestPeerAdmin@org1 --networkAdmin admin --networkAdminEnrollSecret adminpw --archiveFile ${bnaFile} --file ${card}`); + return CodeRunner.runCode(`composer card import --file ${card}`); + } +} diff --git a/packages/composer-playground/e2e/utils/operations-helper.ts b/packages/composer-playground/e2e/utils/operations-helper.ts index ec36bb7eee..36ce21a4a4 100644 --- a/packages/composer-playground/e2e/utils/operations-helper.ts +++ b/packages/composer-playground/e2e/utils/operations-helper.ts @@ -17,7 +17,7 @@ import { ExpectedConditions, ElementFinder } from 'protractor'; import { Editor } from '../component/editor'; import { Import } from '../component/import'; import { Replace } from '../component/replace'; -import { Constants } from './constants'; +import { Constants } from '../constants'; export class OperationsHelper { diff --git a/packages/composer-playground/e2e/utils/replace-helper.ts b/packages/composer-playground/e2e/utils/replace-helper.ts index 58ba3b3b6d..fa33aaba7b 100644 --- a/packages/composer-playground/e2e/utils/replace-helper.ts +++ b/packages/composer-playground/e2e/utils/replace-helper.ts @@ -13,7 +13,7 @@ */ import { browser, element, by } from 'protractor'; import { ExpectedConditions } from 'protractor'; -import { Constants } from './constants'; +import { Constants } from '../constants'; export class ReplaceModalHelper { diff --git a/packages/composer-playground/e2e/verdaccio/.gitignore b/packages/composer-playground/e2e/verdaccio/.gitignore new file mode 100644 index 0000000000..0d1f095607 --- /dev/null +++ b/packages/composer-playground/e2e/verdaccio/.gitignore @@ -0,0 +1,2 @@ +# Verdaccio serve location +/storage diff --git a/packages/composer-playground/e2e/fabric/scripts/config.yaml b/packages/composer-playground/e2e/verdaccio/config.yaml similarity index 98% rename from packages/composer-playground/e2e/fabric/scripts/config.yaml rename to packages/composer-playground/e2e/verdaccio/config.yaml index c55cc9a2fa..6cf039af4c 100644 --- a/packages/composer-playground/e2e/fabric/scripts/config.yaml +++ b/packages/composer-playground/e2e/verdaccio/config.yaml @@ -7,6 +7,7 @@ # # path to a directory with all packages storage: ./storage +max_body_size: 99999mb auth: htpasswd: file: ./htpasswd diff --git a/packages/composer-playground/e2e/fabric/scripts/npm_serve.js b/packages/composer-playground/e2e/verdaccio/npm_serve.js similarity index 67% rename from packages/composer-playground/e2e/fabric/scripts/npm_serve.js rename to packages/composer-playground/e2e/verdaccio/npm_serve.js index 4cb5754d78..acc0cfb90a 100644 --- a/packages/composer-playground/e2e/fabric/scripts/npm_serve.js +++ b/packages/composer-playground/e2e/verdaccio/npm_serve.js @@ -15,8 +15,9 @@ 'use strict'; const exec = require('child_process').exec; +const path = require('path'); -const lernaFile = require('../../../../../lerna.json'); +const lernaFile = require('../../../../lerna.json'); let version = lernaFile.version; /** @@ -53,32 +54,46 @@ let packages = [ 'composer-runtime-hlfv1', 'composer-connector-hlfv1', 'composer-admin', + 'composer-wallet-inmemory', + 'composer-wallet-filesystem', 'composer-client', - 'loopback-connector-composer', - 'composer-rest-server', 'composer-report', - 'composer-cli']; + 'composer-cli', + 'composer-connector-server', + 'composer-playground-api', + 'composer-playground' +]; // Packages to be installed in integration test(s) let testPackages = [ 'composer-cli', - 'composer-runtime-hlfv1', - 'composer-runtime' + 'composer-playground' ]; -return packages.reduce((promiseChain, p) => { - // Set registry and publish - return promiseChain.then(() => { - console.log('Publishing package ' + p + ' to local npm server'); - return invokeCmd('npm publish --registry http://localhost:4873 ../' + p); - }); -}, Promise.resolve()) +return invokeCmd(path.join(__dirname, '../scripts/cleanTestFolders.sh')) +.then(() => { + return invokeCmd(path.join(__dirname, '../scripts/configureGateway.sh')); +}) +.then(() => { + return packages.reduce((promiseChain, p) => { + // Set registry and publish + return promiseChain.then(() => { + // eslint-disable-next-line no-console + console.log('Publishing package ' + p + ' to local npm server'); + return invokeCmd('npm publish --registry http://localhost:4874 ../' + p); + }); + }, Promise.resolve()); +}) .then(() => { // Globally install test packages return testPackages.reduce((promiseChain, p) => { return promiseChain.then(() => { + // eslint-disable-next-line no-console console.log('installing package ' + p + '@' + version + ' from npm server'); - return invokeCmd('npm install --registry http://localhost:4873 -g ' + p + '@' + version); + return invokeCmd('npm install --registry http://localhost:4874 -g ' + p + '@' + version); }); }, Promise.resolve()); +}) +.catch((err) => { + process.exit(1); }); diff --git a/packages/composer-playground/package.json b/packages/composer-playground/package.json index 58bbf3b7bd..79dcdd1f62 100644 --- a/packages/composer-playground/package.json +++ b/packages/composer-playground/package.json @@ -52,9 +52,11 @@ "clean": "npm cache clean && npm run rimraf -- node_modules doc coverage dist", "docker": "docker", "docs": "npm run typedoc -- --options typedoc.json --exclude '**/*.spec.ts' ./src/", - "e2e:nobuild": "node ./e2e/test.js", - "e2e:main": "npm run build:prod && node ./e2e/test.js && npm run e2e:fabric", - "e2e:fabric": "npm run start_verdaccio && node ./e2e/fabric/scripts/npm_serve && node ./e2e/test.js --fabric", + "disabled_preversion": "npm test", + "e2e:web": "ts-node ./e2e/test.ts", + "e2e:main": "npm run build:prod && npm run e2e:web && npm run e2e:fabric", + "e2e:fabric-local": "./e2e/scripts/setupFabric.sh && ts-node ./e2e/test.ts --fabric && npm run stop_verdaccio", + "e2e:fabric": "./e2e/scripts/setupFabric.sh && npm run start_verdaccio && node ./e2e/verdaccio/npm_serve && ts-node ./e2e/test.ts --fabric && npm run stop_verdaccio", "eslint": "eslint", "github-deploy:dev": "webpack --config config/webpack.github-deploy.js --progress --profile --env.githubDev", "github-deploy:prod": "webpack --config config/webpack.github-deploy.js --progress --profile --env.githubProd", @@ -69,7 +71,6 @@ "preclean:install": "npm run clean", "preclean:start": "npm run clean", "pree2e": "npm run webdriver:update -- --standalone", - "disabled_preversion": "npm test", "rimraf": "rimraf", "server:dev:hmr": "npm run server:dev -- --inline --hot", "server:dev": "webpack-dev-server --config config/webpack.dev.js --progress --profile --watch --content-base src/", @@ -78,7 +79,8 @@ "server": "npm run server:dev", "start:hmr": "npm run server:dev:hmr", "start": "npm run server:dev", - "start_verdaccio": "echo '//localhost:4873/:_authToken=\"foo\"' > ${HOME}/.npmrc && echo fetch-retries=10 >> ${HOME}/.npmrc && verdaccio -l 0.0.0.0:4873 -c ./e2e/fabric/scripts/config.yaml >/dev/null &", + "start_verdaccio": "PM2_HOME=.pm2 pm2 start verdaccio -- -l 0.0.0.0:4874 -c ./e2e/verdaccio/config.yaml >/dev/null &", + "stop_verdaccio": "PM2_HOME=.pm2 pm2 delete verdaccio || true", "test": "karma start ./config/karma.conf.js", "tslint": "tslint", "typedoc": "typedoc", @@ -202,6 +204,7 @@ "node-sass": "4.5.3", "object-hash": "1.1.8", "parse5": "3.0.1", + "pm2": "2.10.1", "protractor": "5.1.2", "raw-loader": "0.5.1", "rimraf": "2.5.4", @@ -216,6 +219,7 @@ "sleep": "5.1.1", "source-map-loader": "0.1.5", "spa-http-server": "0.9.0", + "strip-ansi": "3.0.1", "string-replace-loader": "1.0.5", "style-loader": "0.13.1", "svg-sprite-loader": "0.3.0", diff --git a/packages/composer-playground/protractor.fabric.conf.js b/packages/composer-playground/protractor.fabric.conf.js index a87036b1cf..0c2a432a52 100644 --- a/packages/composer-playground/protractor.fabric.conf.js +++ b/packages/composer-playground/protractor.fabric.conf.js @@ -7,7 +7,7 @@ exports.config = { allScriptsTimeout: 20000, framework: 'jasmine', directConnect: true, - baseUrl: 'http://127.0.0.1:3001', + baseUrl: 'http://127.0.0.1:3002', specs: ['./e2e/specs/round-trip.spec.ts', './e2e/specs/playground-tutorial.spec.ts'], capabilities: { @@ -17,7 +17,7 @@ exports.config = { download: { 'prompt_for_download': false, 'directory_upgrade': true, - 'default_directory': './e2e/downloads' + 'default_directory': './e2e/downloads' //matches that specified in /utils/constants.ts file } } } diff --git a/packages/composer-tests-integration/package.json b/packages/composer-tests-integration/package.json index d8b6bae54e..055dfa9abb 100644 --- a/packages/composer-tests-integration/package.json +++ b/packages/composer-tests-integration/package.json @@ -12,6 +12,7 @@ "licchk": "license-check", "postlicchk": "npm run lint", "lint": "eslint .", + "test": "exit 0", "test-tagged": "cucumber-js --tags @busnet", "test-inner": "cucumber-js", "test-inner-nohsm": "cucumber-js --tags 'not @hsm'", diff --git a/packages/composer-tests-integration/scripts/run-integration-tests.sh b/packages/composer-tests-integration/scripts/run-integration-tests.sh index e472f455b7..0fd8871058 100755 --- a/packages/composer-tests-integration/scripts/run-integration-tests.sh +++ b/packages/composer-tests-integration/scripts/run-integration-tests.sh @@ -10,11 +10,13 @@ ARCH=`uname -m` # Grab the parent (root) directory. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +# Switch into the integration tests directory. +cd "${DIR}" + # Delete any existing configuration. -rm -fr ./verdaccio -rm -fr ./storage -rm -fr ./scripts/storage -rm -fr ${HOME}/.config/verdaccio +rm -rf ./pm2 +rm -rf ./scripts/storage +rm -rf ${HOME}/.config/verdaccio rm -rf ${HOME}/.composer/cards/Test* rm -rf ${HOME}/.composer/client-data/Test* rm -rf ${HOME}/.composer/cards/bob* @@ -41,11 +43,6 @@ if [ "${DOCKER_FILE}" != "" ]; then cd "${DIR}" fi - - -# Switch into the integration tests directory. -cd "${DIR}" - # Barf if we don't recognize this test suite. if [ "${INTEST}" = "" ]; then echo You must set INTEST to 'hlfv1' as it is the only supported test item @@ -146,9 +143,9 @@ for INTEST in $(echo ${INTEST} | tr "," " "); do # Run the integration tests. if [[ ${INTEST} == *nohsm ]]; then - npm run int-test-nohsm 2>&1 | tee + npm run int-test-nohsm 2>&1 || : else - npm run int-test 2>&1 | tee + npm run int-test 2>&1 || : fi # Stop all test programs. @@ -162,9 +159,9 @@ for INTEST in $(echo ${INTEST} | tr "," " "); do fi # Delete any written configuration. - rm -fr ./verdaccio - rm -fr ./storage - rm -fr ${HOME}/.config/verdaccio + rm -rf ./pm2 + rm -rf ./scripts/storage + rm -rf ${HOME}/.config/verdaccio rm -rf ${HOME}/.composer/cards/Test* rm -rf ${HOME}/.composer/client-data/Test* rm -rf ${HOME}/.composer/cards/bob*