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

Commit bc9e33d

Browse files
Simon Stonenklincoln
authored andcommitted
Enable simple JSON.stringify serialization of resources (resolves #3092) (#3600)
* Enable simple JSON.stringify serialization of resources (resolves #3092) Signed-off-by: Simon Stone <[email protected]> * Fix the default serializer options for registry actions Signed-off-by: Simon Stone <[email protected]>
1 parent 0cbccdb commit bc9e33d

File tree

23 files changed

+505
-283
lines changed

23 files changed

+505
-283
lines changed

packages/composer-common/api.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class Relationship extends Identifiable {
201201
class Resource extends Identifiable {
202202
+ String toString()
203203
+ boolean isResource()
204+
+ Object toJSON()
204205
}
205206
class Typed {
206207
+ string getType()
@@ -237,9 +238,12 @@ class ModelManager {
237238
+ ParticipantDeclaration[] getParticipantDeclarations(Boolean)
238239
+ EnumDeclaration[] getEnumDeclarations(Boolean)
239240
+ ConceptDeclaration[] getConceptDeclarations(Boolean)
241+
+ Factory getFactory()
242+
+ Serializer getSerializer()
240243
}
241244
class Serializer {
242245
+ void constructor(Factory,ModelManager)
246+
+ void setDefaultOptions(Object)
243247
+ Object toJSON(Resource,Object,boolean,boolean,boolean,boolean) throws Error
244248
+ Resource fromJSON(Object,Object,boolean,boolean)
245249
}

packages/composer-common/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#
1212
# Note that the latest public API is documented using JSDocs and is available in api.txt.
1313
#
14+
15+
Version 0.19.1 {f6814276d318be3b4ed0f6212bc77c6f} 2018-04-06
16+
- Enable simple JSON.stringify serialization of resources
17+
1418
Version 0.18.2 {354087f4e85b14e02e7c8284cca2583f} 2018-03-20
1519
- Add CouchDB index compiler
1620

packages/composer-common/lib/businessnetworkdefinition.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,26 @@
1616

1717
const AclFile = require('./acl/aclfile');
1818
const AclManager = require('./aclmanager');
19-
const QueryFile = require('./query/queryfile');
20-
const QueryManager = require('./querymanager');
2119
const BusinessNetworkMetadata = require('./businessnetworkmetadata');
22-
const Factory = require('./factory');
2320
const fs = require('fs');
2421
const fsPath = require('path');
2522
const Introspector = require('./introspect/introspector');
2623
const JSZip = require('jszip');
2724
const Logger = require('./log/logger');
28-
const ModelManager = require('./modelmanager');
2925
const minimatch = require('minimatch');
26+
const ModelManager = require('./modelmanager');
27+
const QueryFile = require('./query/queryfile');
28+
const QueryManager = require('./querymanager');
3029
const ScriptManager = require('./scriptmanager');
3130
const semver = require('semver');
32-
const Serializer = require('./serializer');
33-
const nodeUtil = require('util');
31+
const thenify = require('thenify');
32+
const util = require('util');
33+
3434
const ENCODING = 'utf8';
3535
const LOG = Logger.getLog('BusinessNetworkDefinition');
36-
37-
const thenify = require('thenify');
3836
const mkdirp = thenify(require('mkdirp'));
3937

38+
4039
/** define a help function that will filter out files
4140
* that are inside a node_modules directory under the path
4241
* we are processing
@@ -116,12 +115,12 @@ class BusinessNetworkDefinition {
116115
}
117116

118117
this.modelManager = new ModelManager();
118+
this.factory = this.modelManager.getFactory();
119+
this.serializer = this.modelManager.getSerializer();
119120
this.aclManager = new AclManager(this.modelManager);
120121
this.queryManager = new QueryManager(this.modelManager);
121122
this.scriptManager = new ScriptManager(this.modelManager);
122123
this.introspector = new Introspector(this.modelManager);
123-
this.factory = new Factory(this.modelManager);
124-
this.serializer = new Serializer(this.factory, this.modelManager);
125124

126125
this.metadata = new BusinessNetworkMetadata(packageJson,readme);
127126
LOG.exit(method);
@@ -718,7 +717,7 @@ class BusinessNetworkDefinition {
718717
mode: createFileMode
719718
};
720719

721-
const writeFile = nodeUtil.promisify(fs.writeFile);
720+
const writeFile = util.promisify(fs.writeFile);
722721

723722
const promises = [];
724723

packages/composer-common/lib/model/resource.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ class Resource extends Identifiable {
7171
return true;
7272
}
7373

74+
/**
75+
* Serialize this resource into a JavaScript object suitable for serialization to JSON,
76+
* using the default options for the serializer. If you need to set additional options
77+
* for the serializer, use the {@link Serializer#toJSON} method instead.
78+
* @return {Object} A JavaScript object suitable for serialization to JSON.
79+
*/
80+
toJSON() {
81+
return this.getModelManager().getSerializer().toJSON(this);
82+
}
83+
7484
}
7585

7686
module.exports = Resource;

packages/composer-common/lib/modelmanager.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414

1515
'use strict';
1616

17+
const DefaultModelFileLoader = require('./introspect/loaders/defaultmodelfileloader');
18+
const Factory = require('./factory');
1719
const Globalize = require('./globalize');
18-
1920
const IllegalModelException = require('./introspect/illegalmodelexception');
20-
const ModelUtil = require('./modelutil');
21+
const Logger = require('./log/logger');
2122
const ModelFile = require('./introspect/modelfile');
22-
const TypeNotFoundException = require('./typenotfoundexception');
23-
24-
const DefaultModelFileLoader = require('./introspect/loaders/defaultmodelfileloader');
2523
const ModelFileDownloader = require('./introspect/loaders/modelfiledownloader');
26-
27-
const LOG = require('./log/logger').getLog('ModelManager');
24+
const ModelUtil = require('./modelutil');
25+
const Serializer = require('./serializer');
2826
const SYSTEM_MODELS = require('./systemmodel');
27+
const TypeNotFoundException = require('./typenotfoundexception');
28+
29+
const LOG = Logger.getLog('ModelManager');
2930

3031
/**
3132
* Manages the Composer model files.
@@ -61,6 +62,8 @@ class ModelManager {
6162
LOG.entry('constructor');
6263
this.modelFiles = {};
6364
this.addSystemModels();
65+
this.factory = new Factory(this);
66+
this.serializer = new Serializer(this.factory, this);
6467
LOG.exit('constructor');
6568
}
6669

@@ -539,6 +542,22 @@ class ModelManager {
539542
}, []);
540543
}
541544

545+
/**
546+
* Get a factory for creating new instances of types defined in this model manager.
547+
* @return {Factory} A factory for creating new instances of types defined in this model manager.
548+
*/
549+
getFactory() {
550+
return this.factory;
551+
}
552+
553+
/**
554+
* Get a serializer for serializing instances of types defined in this model manager.
555+
* @return {Serializer} A serializer for serializing instances of types defined in this model manager.
556+
*/
557+
getSerializer() {
558+
return this.serializer;
559+
}
560+
542561
}
543562

544563
module.exports = ModelManager;

packages/composer-common/lib/serializer.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const TransactionDeclaration = require('./introspect/transactiondeclaration');
2626
const TypedStack = require('./serializer/typedstack');
2727
const JSONWriter = require('./codegen/jsonwriter');
2828

29+
const baseDefaultOptions = {
30+
validate: true
31+
};
32+
2933
/**
3034
* Serialize Resources instances to/from various formats for long-term storage
3135
* (e.g. on the blockchain).
@@ -34,6 +38,7 @@ const JSONWriter = require('./codegen/jsonwriter');
3438
* @memberof module:composer-common
3539
*/
3640
class Serializer {
41+
3742
/**
3843
* Create a Serializer.
3944
* <strong>Note: Only to be called by framework code. Applications should
@@ -52,6 +57,16 @@ class Serializer {
5257

5358
this.factory = factory;
5459
this.modelManager = modelManager;
60+
this.defaultOptions = Object.assign({}, baseDefaultOptions);
61+
}
62+
63+
/**
64+
* Set the default options for the serializer.
65+
* @param {Object} newDefaultOptions The new default options for the serializer.
66+
*/
67+
setDefaultOptions(newDefaultOptions) {
68+
// Combine the specified default options with the base default
69+
this.defaultOptions = Object.assign({}, baseDefaultOptions, newDefaultOptions);
5570
}
5671

5772
/**
@@ -60,14 +75,14 @@ class Serializer {
6075
* peristent storage.
6176
* </p>
6277
* @param {Resource} resource - The instance to convert to JSON
63-
* @param {Object} options - the optional serialization options.
64-
* @param {boolean} options.validate - validate the structure of the Resource
78+
* @param {Object} [options] - the optional serialization options.
79+
* @param {boolean} [options.validate] - validate the structure of the Resource
6580
* with its model prior to serialization (default to true)
66-
* @param {boolean} options.convertResourcesToRelationships - Convert resources that
81+
* @param {boolean} [options.convertResourcesToRelationships] - Convert resources that
6782
* are specified for relationship fields into relationships, false by default.
68-
* @param {boolean} options.permitResourcesForRelationships - Permit resources in the
83+
* @param {boolean} [options.permitResourcesForRelationships] - Permit resources in the
6984
* place of relationships (serializing them as resources), false by default.
70-
* @param {boolean} options.deduplicateResources - Generate $id for resources and
85+
* @param {boolean} [options.deduplicateResources] - Generate $id for resources and
7186
* if a resources appears multiple times in the object graph only the first instance is
7287
* serialized in full, subsequent instances are replaced with a reference to the $id
7388
* @return {Object} - The Javascript Object that represents the resource
@@ -88,10 +103,7 @@ class Serializer {
88103
const classDeclaration = this.modelManager.getType( resource.getFullyQualifiedType() );
89104

90105
// validate the resource against the model
91-
options = options || {};
92-
if(options.validate === undefined) {
93-
options.validate = true;
94-
}
106+
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
95107
if(options.validate) {
96108
const validator = new ResourceValidator(options);
97109
classDeclaration.accept(validator, parameters);
@@ -143,10 +155,7 @@ class Serializer {
143155
const classDeclaration = this.modelManager.getType(jsonObject.$class);
144156

145157
// default the options.
146-
options = options || {};
147-
if(options.validate === undefined) {
148-
options.validate = true;
149-
}
158+
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
150159

151160
// create a new instance, using the identifier field name as the ID.
152161
let resource;

packages/composer-common/test/businessnetworkdefinition.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ describe('BusinessNetworkDefinition', () => {
119119
});
120120

121121
it('should be able to retrieve acl manager', () => {
122-
businessNetworkDefinition.getAclManager.should.not.be.null;
122+
businessNetworkDefinition.getAclManager().should.not.be.null;
123123
});
124124

125125
it('should be able to retrieve query manager', () => {
126-
businessNetworkDefinition.getQueryManager.should.not.be.null;
126+
businessNetworkDefinition.getQueryManager().should.not.be.null;
127127
});
128128

129129
it('should be able to retrieve identifier', () => {

packages/composer-common/test/model/resource.js

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,76 @@ describe('Resource', function () {
3131
o String vin
3232
-->Person owner
3333
}
34+
transaction ScrapCar {
35+
-->Car car
36+
}
3437
`;
3538

3639
let modelManager = null;
37-
let classDecl = null;
38-
39-
before(function () {
40-
modelManager = new ModelManager();
41-
});
4240

4341
beforeEach(function () {
42+
modelManager = new ModelManager();
4443
modelManager.addModelFile(levelOneModel);
45-
classDecl = modelManager.getType('org.acme.l1.Person');
46-
});
47-
48-
afterEach(function () {
49-
modelManager.clearModelFiles();
5044
});
5145

5246
describe('#getClassDeclaration', function() {
5347
it('should return the class declaraction', function () {
48+
const classDecl = modelManager.getType('org.acme.l1.Person');
5449
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
5550
resource.getClassDeclaration().should.equal(classDecl);
5651
});
5752
});
5853

5954
describe('#toJSON', () => {
60-
it('should throw is toJSON is called', function () {
61-
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
62-
(function () {
63-
resource.toJSON();
64-
}).should.throw(/Use Serializer.toJSON to convert resource instances to JSON objects./);
55+
it('should serialize an asset to a JavaScript object', function () {
56+
const classDecl = modelManager.getType('org.acme.l1.Car');
57+
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Car', '456' );
58+
resource.vin = '456';
59+
resource.owner = modelManager.getFactory().newRelationship('org.acme.l1', 'Person', '123');
60+
resource.toJSON().should.deep.equal({
61+
$class: 'org.acme.l1.Car',
62+
owner: 'resource:org.acme.l1.Person#123',
63+
vin: '456'
64+
});
65+
});
66+
67+
it('should serialize a participant to a JavaScript object', function () {
68+
const classDecl = modelManager.getType('org.acme.l1.Person');
69+
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
70+
resource.ssn = '123';
71+
resource.toJSON().should.deep.equal({
72+
$class: 'org.acme.l1.Person',
73+
ssn: '123'
74+
});
75+
});
76+
77+
it('should serialize a transaction to a JavaScript object', function () {
78+
const classDecl = modelManager.getType('org.acme.l1.ScrapCar');
79+
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'ScrapCar', '789' );
80+
resource.transactionId = '789';
81+
resource.timestamp = new Date(0);
82+
resource.car = modelManager.getFactory().newRelationship('org.acme.l1', 'Car', '456');
83+
resource.toJSON().should.deep.equal({
84+
$class: 'org.acme.l1.ScrapCar',
85+
car: 'resource:org.acme.l1.Car#456',
86+
timestamp: '1970-01-01T00:00:00.000Z',
87+
transactionId: '789'
88+
});
6589
});
6690
});
6791

6892
describe('#isRelationship', () => {
6993
it('should be false', () => {
70-
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
94+
const classDecl = modelManager.getType('org.acme.l1.Person');
95+
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
7196
resource.isRelationship().should.be.false;
7297
});
7398
});
7499

75100
describe('#isResource', () => {
76101
it('should be true', () => {
77-
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
102+
const classDecl = modelManager.getType('org.acme.l1.Person');
103+
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
78104
resource.isResource().should.be.true;
79105
});
80106
});

0 commit comments

Comments
 (0)