Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit fe28347

Browse files
author
Simon Stone
authored
Expose processed transactions through the REST API (#981) (#982)
* Add getAllTransactions() and getTransactionByID() to LoopBack connector * Expose getAllTransactions() and getTransactionByID() via REST server * Remove temporary debugging console.log()
1 parent 8d35587 commit fe28347

File tree

4 files changed

+376
-2
lines changed

4 files changed

+376
-2
lines changed

packages/composer-rest-server/server/boot/composer-discovery.js

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ function registerSystemMethods(app, dataSource) {
125125
const registerMethods = [
126126
registerPingMethod,
127127
registerIssueIdentityMethod,
128-
registerRevokeIdentityMethod
128+
registerRevokeIdentityMethod,
129+
registerGetAllTransactionsMethod,
130+
registerGetTransactionByIDMethod
129131
];
130132
registerMethods.forEach((registerMethod) => {
131133
registerMethod(app, dataSource, System, connector);
@@ -331,6 +333,81 @@ function registerRevokeIdentityMethod(app, dataSource, System, connector) {
331333

332334
}
333335

336+
/**
337+
* Register the 'getAllTransactions' Composer system method.
338+
* @param {Object} app The LoopBack application.
339+
* @param {Object} dataSource The LoopBack data source.
340+
* @param {Object} System The System model class.
341+
* @param {Object} connector The LoopBack connector.
342+
*/
343+
function registerGetAllTransactionsMethod(app, dataSource, System, connector) {
344+
345+
// Define and register the method.
346+
System.getAllTransactions = (options, callback) => {
347+
connector.getAllTransactions(options, callback);
348+
};
349+
System.remoteMethod(
350+
'getAllTransactions', {
351+
description: 'Get all transactions from the transaction registry',
352+
accepts: [{
353+
arg: 'options',
354+
type: 'object',
355+
http: 'optionsFromRequest'
356+
}],
357+
returns: {
358+
type: [ 'object' ],
359+
root: true
360+
},
361+
http: {
362+
verb: 'get',
363+
path: '/transactions'
364+
}
365+
}
366+
);
367+
368+
}
369+
370+
/**
371+
* Register the 'getTransactionByID' Composer system method.
372+
* @param {Object} app The LoopBack application.
373+
* @param {Object} dataSource The LoopBack data source.
374+
* @param {Object} System The System model class.
375+
* @param {Object} connector The LoopBack connector.
376+
*/
377+
function registerGetTransactionByIDMethod(app, dataSource, System, connector) {
378+
379+
// Define and register the method.
380+
System.getTransactionByID = (id, options, callback) => {
381+
connector.getTransactionByID(id, options, callback);
382+
};
383+
System.remoteMethod(
384+
'getTransactionByID', {
385+
description: 'Get the specified transaction from the transaction registry',
386+
accepts: [{
387+
arg: 'id',
388+
type: 'string',
389+
required: true,
390+
http: {
391+
source: 'path'
392+
}
393+
}, {
394+
arg: 'options',
395+
type: 'object',
396+
http: 'optionsFromRequest'
397+
}],
398+
returns: {
399+
type: 'object',
400+
root: true
401+
},
402+
http: {
403+
verb: 'get',
404+
path: '/transactions/:id'
405+
}
406+
}
407+
);
408+
409+
}
410+
334411
/**
335412
* Discover all of the model definitions in the specified LoopBack data source.
336413
* @param {Object} dataSource The LoopBack data source.
@@ -503,7 +580,7 @@ module.exports = function (app, callback) {
503580

504581
// Apply any required updates to the specified model schema.
505582
modelSchema = updateModelSchema(modelSchema);
506-
console.log(modelSchema);
583+
507584
// This call creates the model class from the model schema.
508585
let model = app.loopback.createModel(modelSchema);
509586

packages/composer-rest-server/test/system.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,56 @@ describe('System REST API unit tests', () => {
4040
$class: 'org.acme.bond.Member',
4141
memberId: 'MEMBER_2',
4242
name: 'Bob'
43+
}, {
44+
$class: 'org.acme.bond.Issuer',
45+
memberId: 'ISSUER_1',
46+
name: 'Charlie'
47+
}, {
48+
$class: 'org.acme.bond.Issuer',
49+
memberId: 'ISSUER_2',
50+
name: 'Dave'
51+
}];
52+
53+
const transactionData = [{
54+
$class: 'org.acme.bond.PublishBond',
55+
ISINCode: 'ISIN_1',
56+
bond: {
57+
$class: 'org.acme.bond.Bond',
58+
instrumentId: [ 'INST_1' ],
59+
exchangeId: [ 'EXCHG_1' ],
60+
maturity: '1970-01-01T00:00:00.000Z',
61+
parValue: 1.0,
62+
faceAmount: 1.0,
63+
paymentFrequency: {
64+
$class: 'org.acme.bond.PaymentFrequency',
65+
periodMultiplier: 1,
66+
period: 'DAY'
67+
},
68+
dayCountFraction: 'wat',
69+
issuer: 'resource:org.acme.bond.Issuer#ISSUER_1'
70+
}
71+
}, {
72+
$class: 'org.acme.bond.PublishBond',
73+
ISINCode: 'ISIN_2',
74+
bond: {
75+
$class: 'org.acme.bond.Bond',
76+
instrumentId: [ 'INST_2' ],
77+
exchangeId: [ 'EXCHG_2' ],
78+
maturity: '1970-01-01T00:00:00.000Z',
79+
parValue: 2.0,
80+
faceAmount: 2.0,
81+
paymentFrequency: {
82+
$class: 'org.acme.bond.PaymentFrequency',
83+
periodMultiplier: 2,
84+
period: 'DAY'
85+
},
86+
dayCountFraction: 'wat',
87+
issuer: 'resource:org.acme.bond.Issuer#ISSUER_2'
88+
}
4389
}];
4490

91+
const transactionIds = [];
92+
4593
let app;
4694
let businessNetworkConnection;
4795
let participantRegistry;
@@ -89,8 +137,29 @@ describe('System REST API unit tests', () => {
89137
serializer.fromJSON(participantData[1])
90138
]);
91139
})
140+
.then(() => {
141+
return businessNetworkConnection.getParticipantRegistry('org.acme.bond.Issuer');
142+
})
143+
.then((participantRegistry_) => {
144+
participantRegistry = participantRegistry_;
145+
return participantRegistry.addAll([
146+
serializer.fromJSON(participantData[2]),
147+
serializer.fromJSON(participantData[3])
148+
]);
149+
})
92150
.then(() => {
93151
return businessNetworkConnection.issueIdentity('org.acme.bond.Member#MEMBER_2', 'bob1', { issuer: true });
152+
})
153+
.then(() => {
154+
return transactionData.reduce((promise, transaction) => {
155+
return promise.then(() => {
156+
const tx = serializer.fromJSON(transaction);
157+
return businessNetworkConnection.submitTransaction(tx)
158+
.then(() => {
159+
transactionIds.push(tx.getIdentifier());
160+
});
161+
});
162+
}, Promise.resolve());
94163
});
95164
});
96165

@@ -174,4 +243,47 @@ describe('System REST API unit tests', () => {
174243

175244
});
176245

246+
describe('GET /transactions', () => {
247+
248+
it('should return all of the transactions', () => {
249+
return chai.request(app)
250+
.get('/api/system/transactions')
251+
.then((res) => {
252+
res.should.be.json;
253+
res.body.sort((a, b) => {
254+
return a.ISINCode.localeCompare(b.ISINCode);
255+
}).map((tx) => {
256+
delete tx.transactionId;
257+
delete tx.timestamp;
258+
return tx;
259+
}).should.deep.equal(transactionData);
260+
});
261+
});
262+
263+
});
264+
265+
describe('GET /transactions/:id', () => {
266+
267+
it('should return the specified transaction', () => {
268+
return chai.request(app)
269+
.get('/api/system/transactions/' + transactionIds[0])
270+
.then((res) => {
271+
res.should.be.json;
272+
const tx = res.body;
273+
delete tx.transactionId;
274+
delete tx.timestamp;
275+
tx.should.deep.equal(transactionData[0]);
276+
});
277+
});
278+
279+
it('should return a 404 if the specified transaction does not exist', () => {
280+
return chai.request(app)
281+
.get('/api/system/transactions/LOL')
282+
.catch((err) => {
283+
err.response.should.have.status(404);
284+
});
285+
});
286+
287+
});
288+
177289
});

packages/loopback-connector-composer/lib/businessnetworkconnector.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,63 @@ class BusinessNetworkConnector extends Connector {
792792
});
793793
}
794794

795+
/**
796+
* Get all of the transactions from the transaction registry.
797+
* @param {Object} options The LoopBack options.
798+
* @param {function} callback The callback to call when complete.
799+
* @returns {Promise} A promise that is resolved when complete.
800+
*/
801+
getAllTransactions(options, callback) {
802+
debug('getAllTransactions', options);
803+
return this.ensureConnected(options)
804+
.then((businessNetworkConnection) => {
805+
return businessNetworkConnection.getTransactionRegistry();
806+
})
807+
.then((transactionRegistry) => {
808+
return transactionRegistry.getAll();
809+
})
810+
.then((transactions) => {
811+
const result = transactions.map((transaction) => {
812+
return this.serializer.toJSON(transaction);
813+
});
814+
callback(null, result);
815+
})
816+
.catch((error) => {
817+
debug('getAllTransactions', 'error thrown doing getAllTransactions', error);
818+
callback(error);
819+
});
820+
}
821+
822+
/**
823+
* Get the transaction with the specified ID from the transaction registry.
824+
* @param {string} id The ID for the transaction.
825+
* @param {Object} options The LoopBack options.
826+
* @param {function} callback The callback to call when complete.
827+
* @returns {Promise} A promise that is resolved when complete.
828+
*/
829+
getTransactionByID(id, options, callback) {
830+
debug('getTransactionByID', options);
831+
return this.ensureConnected(options)
832+
.then((businessNetworkConnection) => {
833+
return businessNetworkConnection.getTransactionRegistry();
834+
})
835+
.then((transactionRegistry) => {
836+
return transactionRegistry.get(id);
837+
})
838+
.then((transaction) => {
839+
const result = this.serializer.toJSON(transaction);
840+
callback(null, result);
841+
})
842+
.catch((error) => {
843+
debug('getTransactionByID', 'error thrown doing getTransactionByID', error);
844+
if (error.message.match(/does not exist/)) {
845+
error.statusCode = error.status = 404;
846+
}
847+
callback(error);
848+
});
849+
850+
}
851+
795852
/**
796853
* Retrieve the list of all available model names, or the model names in a
797854
* specified namespace.

0 commit comments

Comments
 (0)