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

Commit

Permalink
Enable simple JSON.stringify serialization of resources (resolves #30…
Browse files Browse the repository at this point in the history
…92) (#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]>
  • Loading branch information
Simon Stone authored and nklincoln committed Apr 11, 2018
1 parent 0cbccdb commit bc9e33d
Show file tree
Hide file tree
Showing 23 changed files with 505 additions and 283 deletions.
4 changes: 4 additions & 0 deletions packages/composer-common/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class Relationship extends Identifiable {
class Resource extends Identifiable {
+ String toString()
+ boolean isResource()
+ Object toJSON()
}
class Typed {
+ string getType()
Expand Down Expand Up @@ -237,9 +238,12 @@ class ModelManager {
+ ParticipantDeclaration[] getParticipantDeclarations(Boolean)
+ EnumDeclaration[] getEnumDeclarations(Boolean)
+ ConceptDeclaration[] getConceptDeclarations(Boolean)
+ Factory getFactory()
+ Serializer getSerializer()
}
class Serializer {
+ void constructor(Factory,ModelManager)
+ void setDefaultOptions(Object)
+ Object toJSON(Resource,Object,boolean,boolean,boolean,boolean) throws Error
+ Resource fromJSON(Object,Object,boolean,boolean)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/composer-common/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#

Version 0.19.1 {f6814276d318be3b4ed0f6212bc77c6f} 2018-04-06
- Enable simple JSON.stringify serialization of resources

Version 0.18.2 {354087f4e85b14e02e7c8284cca2583f} 2018-03-20
- Add CouchDB index compiler

Expand Down
21 changes: 10 additions & 11 deletions packages/composer-common/lib/businessnetworkdefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,26 @@

const AclFile = require('./acl/aclfile');
const AclManager = require('./aclmanager');
const QueryFile = require('./query/queryfile');
const QueryManager = require('./querymanager');
const BusinessNetworkMetadata = require('./businessnetworkmetadata');
const Factory = require('./factory');
const fs = require('fs');
const fsPath = require('path');
const Introspector = require('./introspect/introspector');
const JSZip = require('jszip');
const Logger = require('./log/logger');
const ModelManager = require('./modelmanager');
const minimatch = require('minimatch');
const ModelManager = require('./modelmanager');
const QueryFile = require('./query/queryfile');
const QueryManager = require('./querymanager');
const ScriptManager = require('./scriptmanager');
const semver = require('semver');
const Serializer = require('./serializer');
const nodeUtil = require('util');
const thenify = require('thenify');
const util = require('util');

const ENCODING = 'utf8';
const LOG = Logger.getLog('BusinessNetworkDefinition');

const thenify = require('thenify');
const mkdirp = thenify(require('mkdirp'));


/** define a help function that will filter out files
* that are inside a node_modules directory under the path
* we are processing
Expand Down Expand Up @@ -116,12 +115,12 @@ class BusinessNetworkDefinition {
}

this.modelManager = new ModelManager();
this.factory = this.modelManager.getFactory();
this.serializer = this.modelManager.getSerializer();
this.aclManager = new AclManager(this.modelManager);
this.queryManager = new QueryManager(this.modelManager);
this.scriptManager = new ScriptManager(this.modelManager);
this.introspector = new Introspector(this.modelManager);
this.factory = new Factory(this.modelManager);
this.serializer = new Serializer(this.factory, this.modelManager);

this.metadata = new BusinessNetworkMetadata(packageJson,readme);
LOG.exit(method);
Expand Down Expand Up @@ -718,7 +717,7 @@ class BusinessNetworkDefinition {
mode: createFileMode
};

const writeFile = nodeUtil.promisify(fs.writeFile);
const writeFile = util.promisify(fs.writeFile);

const promises = [];

Expand Down
10 changes: 10 additions & 0 deletions packages/composer-common/lib/model/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ class Resource extends Identifiable {
return true;
}

/**
* Serialize this resource into a JavaScript object suitable for serialization to JSON,
* using the default options for the serializer. If you need to set additional options
* for the serializer, use the {@link Serializer#toJSON} method instead.
* @return {Object} A JavaScript object suitable for serialization to JSON.
*/
toJSON() {
return this.getModelManager().getSerializer().toJSON(this);
}

}

module.exports = Resource;
33 changes: 26 additions & 7 deletions packages/composer-common/lib/modelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@

'use strict';

const DefaultModelFileLoader = require('./introspect/loaders/defaultmodelfileloader');
const Factory = require('./factory');
const Globalize = require('./globalize');

const IllegalModelException = require('./introspect/illegalmodelexception');
const ModelUtil = require('./modelutil');
const Logger = require('./log/logger');
const ModelFile = require('./introspect/modelfile');
const TypeNotFoundException = require('./typenotfoundexception');

const DefaultModelFileLoader = require('./introspect/loaders/defaultmodelfileloader');
const ModelFileDownloader = require('./introspect/loaders/modelfiledownloader');

const LOG = require('./log/logger').getLog('ModelManager');
const ModelUtil = require('./modelutil');
const Serializer = require('./serializer');
const SYSTEM_MODELS = require('./systemmodel');
const TypeNotFoundException = require('./typenotfoundexception');

const LOG = Logger.getLog('ModelManager');

/**
* Manages the Composer model files.
Expand Down Expand Up @@ -61,6 +62,8 @@ class ModelManager {
LOG.entry('constructor');
this.modelFiles = {};
this.addSystemModels();
this.factory = new Factory(this);
this.serializer = new Serializer(this.factory, this);
LOG.exit('constructor');
}

Expand Down Expand Up @@ -539,6 +542,22 @@ class ModelManager {
}, []);
}

/**
* Get a factory for creating new instances of types defined in this model manager.
* @return {Factory} A factory for creating new instances of types defined in this model manager.
*/
getFactory() {
return this.factory;
}

/**
* Get a serializer for serializing instances of types defined in this model manager.
* @return {Serializer} A serializer for serializing instances of types defined in this model manager.
*/
getSerializer() {
return this.serializer;
}

}

module.exports = ModelManager;
35 changes: 22 additions & 13 deletions packages/composer-common/lib/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const TransactionDeclaration = require('./introspect/transactiondeclaration');
const TypedStack = require('./serializer/typedstack');
const JSONWriter = require('./codegen/jsonwriter');

const baseDefaultOptions = {
validate: true
};

/**
* Serialize Resources instances to/from various formats for long-term storage
* (e.g. on the blockchain).
Expand All @@ -34,6 +38,7 @@ const JSONWriter = require('./codegen/jsonwriter');
* @memberof module:composer-common
*/
class Serializer {

/**
* Create a Serializer.
* <strong>Note: Only to be called by framework code. Applications should
Expand All @@ -52,6 +57,16 @@ class Serializer {

this.factory = factory;
this.modelManager = modelManager;
this.defaultOptions = Object.assign({}, baseDefaultOptions);
}

/**
* Set the default options for the serializer.
* @param {Object} newDefaultOptions The new default options for the serializer.
*/
setDefaultOptions(newDefaultOptions) {
// Combine the specified default options with the base default
this.defaultOptions = Object.assign({}, baseDefaultOptions, newDefaultOptions);
}

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

// validate the resource against the model
options = options || {};
if(options.validate === undefined) {
options.validate = true;
}
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
if(options.validate) {
const validator = new ResourceValidator(options);
classDeclaration.accept(validator, parameters);
Expand Down Expand Up @@ -143,10 +155,7 @@ class Serializer {
const classDeclaration = this.modelManager.getType(jsonObject.$class);

// default the options.
options = options || {};
if(options.validate === undefined) {
options.validate = true;
}
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;

// create a new instance, using the identifier field name as the ID.
let resource;
Expand Down
4 changes: 2 additions & 2 deletions packages/composer-common/test/businessnetworkdefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ describe('BusinessNetworkDefinition', () => {
});

it('should be able to retrieve acl manager', () => {
businessNetworkDefinition.getAclManager.should.not.be.null;
businessNetworkDefinition.getAclManager().should.not.be.null;
});

it('should be able to retrieve query manager', () => {
businessNetworkDefinition.getQueryManager.should.not.be.null;
businessNetworkDefinition.getQueryManager().should.not.be.null;
});

it('should be able to retrieve identifier', () => {
Expand Down
60 changes: 43 additions & 17 deletions packages/composer-common/test/model/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,50 +31,76 @@ describe('Resource', function () {
o String vin
-->Person owner
}
transaction ScrapCar {
-->Car car
}
`;

let modelManager = null;
let classDecl = null;

before(function () {
modelManager = new ModelManager();
});

beforeEach(function () {
modelManager = new ModelManager();
modelManager.addModelFile(levelOneModel);
classDecl = modelManager.getType('org.acme.l1.Person');
});

afterEach(function () {
modelManager.clearModelFiles();
});

describe('#getClassDeclaration', function() {
it('should return the class declaraction', function () {
const classDecl = modelManager.getType('org.acme.l1.Person');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
resource.getClassDeclaration().should.equal(classDecl);
});
});

describe('#toJSON', () => {
it('should throw is toJSON is called', function () {
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
(function () {
resource.toJSON();
}).should.throw(/Use Serializer.toJSON to convert resource instances to JSON objects./);
it('should serialize an asset to a JavaScript object', function () {
const classDecl = modelManager.getType('org.acme.l1.Car');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Car', '456' );
resource.vin = '456';
resource.owner = modelManager.getFactory().newRelationship('org.acme.l1', 'Person', '123');
resource.toJSON().should.deep.equal({
$class: 'org.acme.l1.Car',
owner: 'resource:org.acme.l1.Person#123',
vin: '456'
});
});

it('should serialize a participant to a JavaScript object', function () {
const classDecl = modelManager.getType('org.acme.l1.Person');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
resource.ssn = '123';
resource.toJSON().should.deep.equal({
$class: 'org.acme.l1.Person',
ssn: '123'
});
});

it('should serialize a transaction to a JavaScript object', function () {
const classDecl = modelManager.getType('org.acme.l1.ScrapCar');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'ScrapCar', '789' );
resource.transactionId = '789';
resource.timestamp = new Date(0);
resource.car = modelManager.getFactory().newRelationship('org.acme.l1', 'Car', '456');
resource.toJSON().should.deep.equal({
$class: 'org.acme.l1.ScrapCar',
car: 'resource:org.acme.l1.Car#456',
timestamp: '1970-01-01T00:00:00.000Z',
transactionId: '789'
});
});
});

describe('#isRelationship', () => {
it('should be false', () => {
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
const classDecl = modelManager.getType('org.acme.l1.Person');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
resource.isRelationship().should.be.false;
});
});

describe('#isResource', () => {
it('should be true', () => {
const resource = new Resource(modelManager, 'org.acme.l1', 'Person', '123' );
const classDecl = modelManager.getType('org.acme.l1.Person');
const resource = new Resource(modelManager, classDecl, 'org.acme.l1', 'Person', '123' );
resource.isResource().should.be.true;
});
});
Expand Down
Loading

0 comments on commit bc9e33d

Please sign in to comment.