@@ -9,6 +9,168 @@ import {ClientService} from './client.service';
99import { BusinessNetworkDefinition } from 'composer-admin' ;
1010import { AclFile } from 'composer-common' ;
1111
12+ const initialModelFile = `/**
13+ * Defines a data model for a blind vehicle auction
14+ */
15+ namespace org.acme.vehicle.auction
16+
17+ asset Vehicle identified by vin {
18+ o String vin
19+ --> Member owner
20+ }
21+
22+ enum ListingState {
23+ o FOR_SALE
24+ o RESERVE_NOT_MET
25+ o SOLD
26+ }
27+
28+ asset VehicleListing identified by listingId {
29+ o String listingId
30+ o Double reservePrice
31+ o String description
32+ o ListingState state
33+ o Offer[] offers optional
34+ --> Vehicle vehicle
35+ }
36+
37+ abstract participant User identified by email {
38+ o String email
39+ o String firstName
40+ o String lastName
41+ }
42+
43+ participant Member extends User {
44+ o Double balance
45+ }
46+
47+ participant Auctioneer extends User {
48+ }
49+
50+ transaction Offer identified by transactionId {
51+ o String transactionId
52+ o Double bidPrice
53+ --> VehicleListing listing
54+ --> Member member
55+ }
56+
57+ transaction CloseBidding identified by transactionId {
58+ o String transactionId
59+ --> VehicleListing listing
60+ }` ;
61+
62+ const initialScriptFile = `/*
63+ * Licensed under the Apache License, Version 2.0 (the "License");
64+ * you may not use this file except in compliance with the License.
65+ * You may obtain a copy of the License at
66+ *
67+ * http://www.apache.org/licenses/LICENSE-2.0
68+ *
69+ * Unless required by applicable law or agreed to in writing, software
70+ * distributed under the License is distributed on an "AS IS" BASIS,
71+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72+ * See the License for the specific language governing permissions and
73+ * limitations under the License.
74+ */
75+
76+ /**
77+ * Close the bidding for a vehicle listing and choose the
78+ * highest bid that is over the asking price
79+ * @param {org.acme.vehicle.auction.CloseBidding} closeBidding - the closeBidding transaction
80+ * @transaction
81+ */
82+ function closeBidding(closeBidding) {
83+ var listing = closeBidding.listing;
84+ if (listing.state !== 'FOR_SALE') {
85+ throw new Error('Listing is not FOR SALE');
86+ }
87+ // by default we mark the listing as RESERVE_NOT_MET
88+ listing.state = 'RESERVE_NOT_MET';
89+ var highestOffer = null;
90+ var buyer = null;
91+ var seller = null;
92+ if (listing.offers) {
93+ // sort the bids by bidPrice
94+ listing.offers.sort(function(a, b) {
95+ return (b.bidPrice - a.bidPrice);
96+ });
97+ highestOffer = listing.offers[0];
98+ if (highestOffer.bidPrice >= listing.reservePrice) {
99+ // mark the listing as SOLD
100+ listing.state = 'SOLD';
101+ buyer = highestOffer.member;
102+ seller = listing.vehicle.owner;
103+ // update the balance of the seller
104+ console.log('#### seller balance before: ' + seller.balance);
105+ seller.balance += highestOffer.bidPrice;
106+ console.log('#### seller balance after: ' + seller.balance);
107+ // update the balance of the buyer
108+ console.log('#### buyer balance before: ' + buyer.balance);
109+ buyer.balance -= highestOffer.bidPrice;
110+ console.log('#### buyer balance after: ' + buyer.balance);
111+ // transfer the vehicle to the buyer
112+ listing.vehicle.owner = buyer;
113+ // clear the offers
114+ listing.offers = null;
115+ }
116+ }
117+ return getAssetRegistry('org.acme.vehicle.auction.Vehicle')
118+ .then(function(vehicleRegistry) {
119+ // save the vehicle
120+ if (highestOffer) {
121+ return vehicleRegistry.update(listing.vehicle);
122+ } else {
123+ return true;
124+ }
125+ })
126+ .then(function() {
127+ return getAssetRegistry('org.acme.vehicle.auction.VehicleListing')
128+ })
129+ .then(function(vehicleListingRegistry) {
130+ // save the vehicle listing
131+ return vehicleListingRegistry.update(listing);
132+ })
133+ .then(function() {
134+ return getParticipantRegistry('org.acme.vehicle.auction.Member')
135+ })
136+ .then(function(userRegistry) {
137+ // save the buyer
138+ if (listing.state == 'SOLD') {
139+ return userRegistry.updateAll([buyer, seller]);
140+ } else {
141+ return true;
142+ }
143+ });
144+ }
145+
146+ /**
147+ * Make an Offer for a VehicleListing
148+ * @param {org.acme.vehicle.auction.Offer} offer - the offer
149+ * @transaction
150+ */
151+ function makeOffer(offer) {
152+ var listing = offer.listing;
153+ if (listing.state !== 'FOR_SALE') {
154+ throw new Error('Listing is not FOR SALE');
155+ }
156+ if (listing.offers == null) {
157+ listing.offers = [];
158+ }
159+ listing.offers.push(offer);
160+ return getAssetRegistry('org.acme.vehicle.auction.VehicleListing')
161+ .then(function(vehicleListingRegistry) {
162+ // save the vehicle listing
163+ return vehicleListingRegistry.update(listing);
164+ });
165+ }` ;
166+
167+ const initialAclFile = `/**
168+ * Access Control List for the auction network.
169+ */
170+ Auctioneer | org.acme.vehicle.auction | ALL | org.acme.vehicle.auction.Auctioneer | (true) | ALLOW | Allow the auctioneer full access
171+ Member | org.acme.vehicle.auction | READ | org.acme.vehicle.auction.Member | (true) | ALLOW | Allow the member read access
172+ 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
173+ 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` ;
12174
13175@Injectable ( )
14176export class SampleBusinessNetworkService {
@@ -253,6 +415,20 @@ export class SampleBusinessNetworkService {
253415 } ) ;
254416 }
255417
418+ public deployInitialSample ( ) : Promise < any > {
419+ this . adminService . busyStatus$ . next ( 'Deploying sample business network ...' ) ;
420+ let businessNetworkDefinition = new BusinessNetworkDefinition ( 'org.acme.biznet@0.0.1' , 'Acme Business Network' ) ;
421+ let modelManager = businessNetworkDefinition . getModelManager ( ) ;
422+ modelManager . addModelFile ( initialModelFile ) ;
423+ let scriptManager = businessNetworkDefinition . getScriptManager ( ) ;
424+ let thisScript = scriptManager . createScript ( 'lib/logic.js' , 'JS' , initialScriptFile ) ;
425+ scriptManager . addScript ( thisScript ) ;
426+ let aclManager = businessNetworkDefinition . getAclManager ( ) ;
427+ let aclFile = new AclFile ( 'permissions.acl' , modelManager , initialAclFile ) ;
428+ aclManager . setAclFile ( aclFile ) ;
429+ return this . deployBusinessNetwork ( businessNetworkDefinition ) ;
430+ }
431+
256432 public deploySample ( owner : string , repository : string , chosenNetwork : any ) : Promise < any > {
257433 this . adminService . busyStatus$ . next ( 'Deploying sample business network ...' ) ;
258434
@@ -292,8 +468,12 @@ export class SampleBusinessNetworkService {
292468 let aclFile = new AclFile ( acls . name , modelManager , acls . data ) ;
293469 aclManager . setAclFile ( aclFile ) ;
294470 }
295- return this . adminService . update ( businessNetworkDefinition ) ;
296- } )
471+ return this . deployBusinessNetwork ( businessNetworkDefinition ) ;
472+ } ) ;
473+ }
474+
475+ public deployBusinessNetwork ( businessNetworkDefinition : BusinessNetworkDefinition ) : Promise < any > {
476+ return this . adminService . update ( businessNetworkDefinition )
297477 . then ( ( ) => {
298478 return this . clientService . refresh ( ) ;
299479 } )
@@ -304,4 +484,5 @@ export class SampleBusinessNetworkService {
304484 throw error ;
305485 } ) ;
306486 }
487+
307488}
0 commit comments