diff --git a/packages/composer-ui/src/app/initialization.service.ts b/packages/composer-ui/src/app/initialization.service.ts index 83e1cd3e90..fb988f2cf5 100644 --- a/packages/composer-ui/src/app/initialization.service.ts +++ b/packages/composer-ui/src/app/initialization.service.ts @@ -31,10 +31,12 @@ export class InitializationService { }) .then(() => { if (this.adminService.isInitialDeploy()) { - return this.sampleBusinessNetworkService.getSampleNetworkInfo(fabricComposerOwner, fabricComposerRepository, 'packages/CarAuction-Network/') - .then((info) => { - return this.sampleBusinessNetworkService.deploySample(fabricComposerOwner, fabricComposerRepository, info); - }) + // We can't use the Github sample integration for the initial deploy until we figure out the rate limiting! + return this.sampleBusinessNetworkService.deployInitialSample(); + // return this.sampleBusinessNetworkService.getSampleNetworkInfo(fabricComposerOwner, fabricComposerRepository, 'packages/CarAuction-Network/') + // .then((info) => { + // return this.sampleBusinessNetworkService.deploySample(fabricComposerOwner, fabricComposerRepository, info); + // }); } }) .then(() => { diff --git a/packages/composer-ui/src/app/samplebusinessnetwork.service.ts b/packages/composer-ui/src/app/samplebusinessnetwork.service.ts index 9cae80ebe3..46ccf2b5ed 100644 --- a/packages/composer-ui/src/app/samplebusinessnetwork.service.ts +++ b/packages/composer-ui/src/app/samplebusinessnetwork.service.ts @@ -9,6 +9,168 @@ import {ClientService} from './client.service'; import {BusinessNetworkDefinition} from 'composer-admin'; import {AclFile} from 'composer-common'; +const initialModelFile = `/** + * Defines a data model for a blind vehicle auction + */ +namespace org.acme.vehicle.auction + +asset Vehicle identified by vin { + o String vin + --> Member owner +} + +enum ListingState { + o FOR_SALE + o RESERVE_NOT_MET + o SOLD +} + +asset VehicleListing identified by listingId { + o String listingId + o Double reservePrice + o String description + o ListingState state + o Offer[] offers optional + --> Vehicle vehicle +} + +abstract participant User identified by email { + o String email + o String firstName + o String lastName +} + +participant Member extends User { + o Double balance +} + +participant Auctioneer extends User { +} + +transaction Offer identified by transactionId { + o String transactionId + o Double bidPrice + --> VehicleListing listing + --> Member member +} + +transaction CloseBidding identified by transactionId { + o String transactionId + --> VehicleListing listing +}`; + +const initialScriptFile = `/* + * 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. + */ + +/** + * Close the bidding for a vehicle listing and choose the + * highest bid that is over the asking price + * @param {org.acme.vehicle.auction.CloseBidding} closeBidding - the closeBidding transaction + * @transaction + */ +function closeBidding(closeBidding) { + var listing = closeBidding.listing; + if (listing.state !== 'FOR_SALE') { + throw new Error('Listing is not FOR SALE'); + } + // by default we mark the listing as RESERVE_NOT_MET + listing.state = 'RESERVE_NOT_MET'; + var highestOffer = null; + var buyer = null; + var seller = null; + if (listing.offers) { + // sort the bids by bidPrice + listing.offers.sort(function(a, b) { + return (b.bidPrice - a.bidPrice); + }); + highestOffer = listing.offers[0]; + if (highestOffer.bidPrice >= listing.reservePrice) { + // mark the listing as SOLD + listing.state = 'SOLD'; + buyer = highestOffer.member; + seller = listing.vehicle.owner; + // update the balance of the seller + console.log('#### seller balance before: ' + seller.balance); + seller.balance += highestOffer.bidPrice; + console.log('#### seller balance after: ' + seller.balance); + // update the balance of the buyer + console.log('#### buyer balance before: ' + buyer.balance); + buyer.balance -= highestOffer.bidPrice; + console.log('#### buyer balance after: ' + buyer.balance); + // transfer the vehicle to the buyer + listing.vehicle.owner = buyer; + // clear the offers + listing.offers = null; + } + } + return getAssetRegistry('org.acme.vehicle.auction.Vehicle') + .then(function(vehicleRegistry) { + // save the vehicle + if (highestOffer) { + return vehicleRegistry.update(listing.vehicle); + } else { + return true; + } + }) + .then(function() { + return getAssetRegistry('org.acme.vehicle.auction.VehicleListing') + }) + .then(function(vehicleListingRegistry) { + // save the vehicle listing + return vehicleListingRegistry.update(listing); + }) + .then(function() { + return getParticipantRegistry('org.acme.vehicle.auction.Member') + }) + .then(function(userRegistry) { + // save the buyer + if (listing.state == 'SOLD') { + return userRegistry.updateAll([buyer, seller]); + } else { + return true; + } + }); +} + +/** + * Make an Offer for a VehicleListing + * @param {org.acme.vehicle.auction.Offer} offer - the offer + * @transaction + */ +function makeOffer(offer) { + var listing = offer.listing; + if (listing.state !== 'FOR_SALE') { + throw new Error('Listing is not FOR SALE'); + } + if (listing.offers == null) { + listing.offers = []; + } + listing.offers.push(offer); + return getAssetRegistry('org.acme.vehicle.auction.VehicleListing') + .then(function(vehicleListingRegistry) { + // save the vehicle listing + return vehicleListingRegistry.update(listing); + }); +}`; + +const initialAclFile = `/** + * Access Control List for the auction network. + */ +Auctioneer | org.acme.vehicle.auction | ALL | org.acme.vehicle.auction.Auctioneer | (true) | ALLOW | Allow the auctioneer full access +Member | org.acme.vehicle.auction | READ | org.acme.vehicle.auction.Member | (true) | ALLOW | Allow the member read access +VehicleOwner | org.acme.vehicle.auction.Vehicle:v | ALL | org.acme.vehicle.auction.Member:u | (v.owner.getIdentifier() == u.getIdentifier()) | ALLOW | Allow the owner of a vehicle total access +VehicleListingOwner | org.acme.vehicle.auction.VehicleListing:v | ALL | org.acme.vehicle.auction.Member:u | (v.vehicle.owner.getIdentifier() == u.getIdentifier()) | ALLOW | Allow the owner of a vehicle total access to their vehicle listing\n`; @Injectable() export class SampleBusinessNetworkService { @@ -253,6 +415,20 @@ export class SampleBusinessNetworkService { }); } + public deployInitialSample(): Promise { + this.adminService.busyStatus$.next('Deploying sample business network ...'); + let businessNetworkDefinition = new BusinessNetworkDefinition('org.acme.biznet@0.0.1', 'Acme Business Network'); + let modelManager = businessNetworkDefinition.getModelManager(); + modelManager.addModelFile(initialModelFile); + let scriptManager = businessNetworkDefinition.getScriptManager(); + let thisScript = scriptManager.createScript('lib/logic.js', 'JS', initialScriptFile); + scriptManager.addScript(thisScript); + let aclManager = businessNetworkDefinition.getAclManager(); + let aclFile = new AclFile('permissions.acl', modelManager, initialAclFile); + aclManager.setAclFile(aclFile); + return this.deployBusinessNetwork(businessNetworkDefinition); + } + public deploySample(owner: string, repository: string, chosenNetwork: any): Promise < any > { this.adminService.busyStatus$.next('Deploying sample business network ...'); @@ -292,8 +468,12 @@ export class SampleBusinessNetworkService { let aclFile = new AclFile(acls.name, modelManager, acls.data); aclManager.setAclFile(aclFile); } - return this.adminService.update(businessNetworkDefinition); - }) + return this.deployBusinessNetwork(businessNetworkDefinition); + }); + } + + public deployBusinessNetwork(businessNetworkDefinition: BusinessNetworkDefinition): Promise { + return this.adminService.update(businessNetworkDefinition) .then(() => { return this.clientService.refresh(); }) @@ -304,4 +484,5 @@ export class SampleBusinessNetworkService { throw error; }); } + }