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

Commit db9b090

Browse files
Simon Stonenklincoln
authored andcommitted
Add documentation for transaction return types and commit flag (resolves #4165, #4224) (#4230)
Signed-off-by: Simon Stone <[email protected]>
1 parent b9edd1f commit db9b090

File tree

1 file changed

+303
-3
lines changed

1 file changed

+303
-3
lines changed

packages/composer-website/jekylldocs/reference/js_scripts.md

Lines changed: 303 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ async function sampleTransaction(tx) {
175175

176176
In this example, not only can the specific asset referenced by the relationship in the transaction be referenced using `tx.asset`, the specific participant referenced by the `owner` relationship can be referenced using `tx.asset.owner`. In this case, `tx.asset.owner` would resolve to reference a specific participant.
177177

178-
## Promise returns in transaction processor functions
178+
## Handling asynchronous code and promises in transaction processor functions
179179

180180
Similarly to relationships, transaction processor functions will wait for promises to be resolved before committing the transaction. If a promise is rejected, the transaction will fail.
181181

@@ -285,7 +285,7 @@ async function sampleTransaction(tx) {
285285
}
286286
```
287287

288-
### Calling {{site.data.conrefs.hlf_full}} {{site.data.conrefs.hlf_latest}} APIs in transaction processor functions
288+
### Calling {{site.data.conrefs.hlf_full}} APIs in transaction processor functions
289289

290290
To call the {{site.data.conrefs.hlf_full}} API in a transaction processor function, the function `getNativeAPI` must be called, followed by a function from the {{site.data.conrefs.hlf_full}} API. Using the {{site.data.conrefs.hlf_full}} API gives you access to functionality which is not available in the {{site.data.conrefs.composer_full}} API.
291291

@@ -296,7 +296,7 @@ In the example below, the {{site.data.conrefs.hlf_full}} API function `getHistor
296296
For more information on the {{site.data.conrefs.hlf_full}} APIs you can call in a transaction processor function, see the [{{site.data.conrefs.hlf_full}} API documentation](https://fabric-shim.github.io/ChaincodeStub.html).
297297

298298
```javascript
299-
async function simpleNativeHistoryTransaction (transaction) {
299+
async function simpleNativeHistoryTransaction (transaction) {
300300
const id = transaction.assetId;
301301
const nativeSupport = transaction.nativeSupport;
302302

@@ -324,6 +324,306 @@ async function simpleNativeHistoryTransaction (transaction) {
324324
}
325325
```
326326

327+
## Returning data from transaction processor functions
328+
329+
Transaction processor functions can optionally return data to client applications. This can be useful for returning a receipt to the submitter of the transaction, or returning an asset modified by the transaction to avoid a separate lookup of the asset after the transaction has been committed.
330+
331+
The return data for a transaction processor function must be a valid type, either a primitive type (String, Integer, Long, etc.), or a type modelled using the Composer modelling language - a concept, asset, participant, transaction, event or enumeration.
332+
333+
The type of the return data must also be specified on the model for the transaction using the `@returns(Type)` decorator, and the return data must be the last thing returned by the transaction processor function. If you have multiple transaction processor functions for a single transaction, only one of those transaction processor functions can return data. If the return data is missing, or is of the wrong type, then the transaction will fail and will be rejected.
334+
335+
### Returning a primitive type from a transaction processor function
336+
337+
Here is an example of a transaction processor function that returns a String to a client application.
338+
339+
Model file:
340+
341+
namespace org.sample
342+
343+
@returns(String)
344+
transaction MyTransaction {
345+
346+
}
347+
348+
Transaction processor function:
349+
350+
/**
351+
* Handle a transaction that returns a string.
352+
* @param {org.sample.MyTransaction} transaction The transaction.
353+
* @returns {string} The string.
354+
* @transaction
355+
*/
356+
async function myTransaction(transaction) {
357+
return 'hello world!';
358+
}
359+
360+
Client application:
361+
362+
const bnc = new BusinessNetworkConnection();
363+
await bnc.connect('admin@sample-network');
364+
const factory = bnc.getBusinessNetwork().getFactory();
365+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
366+
const string = await bnc.submitTransaction(transaction);
367+
console.log(`transaction returned ${string}`);
368+
369+
Here is an example of a transaction processor function that returns an array of integers to a client application.
370+
371+
Model file:
372+
373+
namespace org.sample
374+
375+
@returns(Integer[])
376+
transaction MyTransaction {
377+
378+
}
379+
380+
Transaction processor function:
381+
382+
/**
383+
* Handle a transaction that returns an array of integers.
384+
* @param {org.sample.MyTransaction} transaction The transaction.
385+
* @returns {number[]} The array of integers.
386+
* @transaction
387+
*/
388+
async function myTransaction(transaction) {
389+
return [1, 2, 3];
390+
}
391+
392+
Client application:
393+
394+
const bnc = new BusinessNetworkConnection();
395+
await bnc.connect('admin@sample-network');
396+
const factory = bnc.getBusinessNetwork().getFactory();
397+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
398+
const integers = await bnc.submitTransaction(transaction);
399+
for (const integer of integers) {
400+
console.log(`transaction returned ${integer}`);
401+
}
402+
403+
### Returning a complex type from a transaction processor function
404+
405+
Here is an example of a transaction processor function that returns a concept to a client application. The same code can be modified to return an asset, participant, transaction or event as well.
406+
407+
Model file:
408+
409+
namespace org.sample
410+
411+
concept MyConcept {
412+
o String value
413+
}
414+
415+
@returns(MyConcept)
416+
transaction MyTransaction {
417+
418+
}
419+
420+
Transaction processor function:
421+
422+
/**
423+
* Handle a transaction that returns a concept.
424+
* @param {org.sample.MyTransaction} transaction The transaction.
425+
* @returns {org.sample.MyConcept} The concept.
426+
* @transaction
427+
*/
428+
async function myTransaction(transaction) {
429+
const factory = getFactory();
430+
const concept = factory.newConcept('org.sample', 'MyConcept');
431+
concept.value = 'hello world!';
432+
return concept;
433+
}
434+
435+
Client application:
436+
437+
const bnc = new BusinessNetworkConnection();
438+
await bnc.connect('admin@sample-network');
439+
const factory = bnc.getBusinessNetwork().getFactory();
440+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
441+
const concept = await bnc.submitTransaction(transaction);
442+
console.log(`transaction returned ${concept.value}`);
443+
444+
Here is an example of a transaction processor function that returns an array of concepts to a client application.
445+
446+
Model file:
447+
448+
namespace org.sample
449+
450+
concept MyConcept {
451+
o String value
452+
}
453+
454+
@returns(MyConcept[])
455+
transaction MyTransaction {
456+
457+
}
458+
459+
Transaction processor function:
460+
461+
/**
462+
* Handle a transaction that returns an array of concepts.
463+
* @param {org.sample.MyTransaction} transaction The transaction.
464+
* @returns {org.sample.MyConcept[]} The array of concepts.
465+
* @transaction
466+
*/
467+
async function myTransaction(transaction) {
468+
const factory = getFactory();
469+
const concept1 = factory.newConcept('org.sample', 'MyConcept');
470+
concept1.value = 'hello alice!';
471+
const concept2 = factory.newConcept('org.sample', 'MyConcept');
472+
concept2.value = 'hello bob!';
473+
const concept3 = factory.newConcept('org.sample', 'MyConcept');
474+
concept3.value = 'hello charlie!';
475+
return [ concept1, concept2, concept3 ];
476+
}
477+
478+
Client application:
479+
480+
const bnc = new BusinessNetworkConnection();
481+
await bnc.connect('admin@sample-network');
482+
const factory = bnc.getBusinessNetwork().getFactory();
483+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
484+
const concepts = await bnc.submitTransaction(transaction);
485+
for (const concept of concepts) {
486+
console.log(`transaction returned ${concept.value}`);
487+
}
488+
489+
### Returning an enumeration from a transaction processor function
490+
491+
Here is an example of a transaction processor function that returns an enumeration to a client application.
492+
493+
Model file:
494+
495+
namespace org.sample
496+
497+
enum MyEnum {
498+
o HELLO
499+
o WORLD
500+
}
501+
502+
@returns(MyEnum)
503+
transaction MyTransaction {
504+
505+
}
506+
507+
Transaction processor function:
508+
509+
/**
510+
* Handle a transaction that returns an enumeration.
511+
* @param {org.sample.MyTransaction} transaction The transaction.
512+
* @returns {org.sample.MyEnum} The enumeration.
513+
* @transaction
514+
*/
515+
async function myTransaction(transaction) {
516+
return 'HELLO';
517+
}
518+
519+
Client application:
520+
521+
const bnc = new BusinessNetworkConnection();
522+
await bnc.connect('admin@sample-network');
523+
const factory = bnc.getBusinessNetwork().getFactory();
524+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
525+
const enum = await bnc.submitTransaction(transaction);
526+
console.log(`transaction returned ${enum}`);
527+
528+
Here is an example of a transaction processor function that returns an array of enumerations to a client application.
529+
530+
Model file:
531+
532+
namespace org.sample
533+
534+
enum MyEnum {
535+
o HELLO
536+
o WORLD
537+
}
538+
539+
@returns(MyEnum[])
540+
transaction MyTransaction {
541+
542+
}
543+
544+
Transaction processor function:
545+
546+
/**
547+
* Handle a transaction that returns an array of enumerations.
548+
* @param {org.sample.MyTransaction} transaction The transaction.
549+
* @returns {org.sample.MyEnum[]} The array of enumerations.
550+
* @transaction
551+
*/
552+
async function myTransaction(transaction) {
553+
return [ 'HELLO', 'WORLD' ];
554+
}
555+
556+
Client application:
557+
558+
const bnc = new BusinessNetworkConnection();
559+
await bnc.connect('admin@sample-network');
560+
const factory = bnc.getBusinessNetwork().getFactory();
561+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
562+
const enums = await bnc.submitTransaction(transaction);
563+
for (const enum of enums) {
564+
console.log(`transaction returned ${enum}`);
565+
}
566+
567+
## Read-only transaction processor functions (query processor functions)
568+
569+
Transactions can be modelled as being read-only by specifying the `@commit(false)` decorator. When a transaction is modelled as read-only, the transaction is submitted as normal, and any transaction processor functions for that transaction are executed as normal. However, the transaction is not committed - it will not be endorsed by multiple peers on the blockchain network, nor will it be sent to the ordering service, nor will it publish any events.
570+
571+
This feature can be useful when the APIs that client applications can use to read data from the business network are too limited for your use case. These APIs include `get(id)` (get by ID), `getAll()` (get all), `exists(id)` (test existence), and `query(q, params)` (execute a complex query). For example, a client application may wish to get all of the assets across multiple business networks deployed to multiple channels in a single call to the blockchain network. Another example is reducing the result set of a query on the "server" (chaincode) side, before returning the result set to the client application, to reduce network traffic and the load on the client application.
572+
573+
Here is an example of a read-only transaction processor function that retrieves a set of assets from the current business network, as well as other business networks, and returns all of the assets to the client application:
574+
575+
Model file:
576+
577+
namespace org.sample
578+
579+
asset MyAsset identified by assetId {
580+
o String assetId
581+
o String value
582+
}
583+
584+
@commit(false)
585+
@returns(MyAsset[])
586+
transaction MyTransaction {
587+
588+
}
589+
590+
Transaction processor function:
591+
592+
/**
593+
* Handle a transaction that returns an array of assets.
594+
* @param {org.sample.MyTransaction} transaction The transaction.
595+
* @returns {org.sample.MyAsset[]} All the assets.
596+
* @transaction
597+
*/
598+
async function myTransaction(transaction) {
599+
const allAssets = [];
600+
const assetRegistry = await getAssetRegistry('org.sample.MyAsset');
601+
const localAssets = await assetRegistry.getAll();
602+
for (const asset of localAssets) {
603+
localAssets.push(asset);
604+
}
605+
const businessNetworkNames = ['other-network-1', 'other-network-2'];
606+
for (const businessNetworkName of businessNetworkNames) {
607+
const response = await getNativeAPI().invokeChaincode(businessNetworkName, ['getAllResourcesInRegistry', 'Asset', 'org.sample.MyAsset'], 'composerchannel');
608+
const json = JSON.parse(response.payload.toString('utf8'));
609+
for (const item of json) {
610+
allAssets.push(getSerializer().fromJSON(item));
611+
}
612+
}
613+
return allAssets;
614+
}
615+
616+
Client application:
617+
618+
const bnc = new BusinessNetworkConnection();
619+
await bnc.connect('admin@sample-network');
620+
const factory = bnc.getBusinessNetwork().getFactory();
621+
const transaction = factory.newTransaction('org.sample', 'MyTransaction');
622+
const assets = await bnc.submitTransaction(transaction);
623+
for (const asset of assets) {
624+
console.log(`transaction returned ${asset.value}`);
625+
}
626+
327627
## What next?
328628

329629
Transaction processor functions can also be used to:

0 commit comments

Comments
 (0)