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

Commit

Permalink
[Master] Move experimental features to 0.20 (#4593)
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Kelsey <[email protected]>
  • Loading branch information
Dave Kelsey authored and nklincoln committed Feb 4, 2019
1 parent 9f836cb commit f2adfab
Show file tree
Hide file tree
Showing 32 changed files with 1,032 additions and 112 deletions.
8 changes: 7 additions & 1 deletion packages/composer-common/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ class ModelManager {
+ DecoratorFactory[] getDecoratorFactories()
+ void addDecoratorFactory(DecoratorFactory)
}
class ReadOnlyDecorator extends Decorator {
+ void constructor(Object) throws IllegalModelException
}
class ReadOnlyDecoratorFactory extends DecoratorFactory {
+ Decorator newDecorator(Object)
}
class ReturnsDecorator extends Decorator {
+ void constructor(Object) throws IllegalModelException
+ string getType()
Expand All @@ -272,7 +278,7 @@ class ReturnsDecoratorFactory extends DecoratorFactory {
class Serializer {
+ void constructor(Factory,ModelManager)
+ void setDefaultOptions(Object)
+ Object toJSON(Resource,Object,boolean,boolean,boolean,boolean) throws Error
+ Object toJSON(Resource,Object,boolean,boolean,boolean,boolean,boolean) throws Error
+ Resource fromJSON(Object,Object,boolean,boolean)
}
class Wallet {
Expand Down
6 changes: 6 additions & 0 deletions packages/composer-common/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#

Version 0.19.20 {308d962120667e982b2600107d0b8b13} 2019-01-29
- Modify readonly decorator to be parameterless

Version 0.19.20 {5ff216eab17b2d990c43636c51a585b5} 2018-12-18
- Add readonly decorator factory

Version 0.19.11 {765772c9b73656c289a15398bccc76fd} 2018-06-29
- Add decorator factories
- Add returns decorator factory
Expand Down
1 change: 1 addition & 0 deletions packages/composer-common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ module.exports.Query = require('./lib/query/query');
module.exports.QueryAnalyzer = require('./lib/query/queryanalyzer.js');
module.exports.QueryFile = require('./lib/query/queryfile');
module.exports.QueryManager = require('./lib/querymanager');
module.exports.ReadOnlyDecoratorFactory = require('./lib/readonlydecoratorfactory');
module.exports.Relationship = require('./lib/model/relationship');
module.exports.RelationshipDeclaration = require('./lib/introspect/relationshipdeclaration');
module.exports.Resource = require('./lib/model/resource');
Expand Down
2 changes: 2 additions & 0 deletions packages/composer-common/lib/businessnetworkdefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const minimatch = require('minimatch');
const ModelManager = require('./modelmanager');
const QueryFile = require('./query/queryfile');
const QueryManager = require('./querymanager');
const ReadOnlyDecoratorFactory = require('./readonlydecoratorfactory');
const ReturnsDecoratorFactory = require('./returnsdecoratorfactory');
const ScriptManager = require('./scriptmanager');
const semver = require('semver');
Expand Down Expand Up @@ -117,6 +118,7 @@ class BusinessNetworkDefinition {

this.modelManager = new ModelManager();
this.modelManager.addDecoratorFactory(new CommitDecoratorFactory());
this.modelManager.addDecoratorFactory(new ReadOnlyDecoratorFactory());
this.modelManager.addDecoratorFactory(new ReturnsDecoratorFactory());
this.factory = this.modelManager.getFactory();
this.serializer = this.modelManager.getSerializer();
Expand Down
34 changes: 22 additions & 12 deletions packages/composer-common/lib/introspect/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,25 +124,35 @@ class Property extends Decorated {
* @return {string} the fully qualified type of this property
*/
getFullyQualifiedTypeName() {

if(this.isPrimitive()) {
return this.type;
}

const parent = this.getParent();
if(!parent) {
throw new Error('Property ' + this.name + ' does not have a parent.');
}
const modelFile = parent.getModelFile();
if(!modelFile) {
throw new Error('Parent of property ' + this.name + ' does not have a ModelFile!');
}
if (this.fullyQualifiedTypeName){
return this.fullyQualifiedTypeName;
} else {
const parent = this.getParent();
if(!parent) {
throw new Error('Property ' + this.name + ' does not have a parent.');
}
const modelFile = parent.getModelFile();
if(!modelFile) {
throw new Error('Parent of property ' + this.name + ' does not have a ModelFile!');
}

const result = modelFile.getFullyQualifiedTypeName(this.type);

if(!result) {
throw new Error('Failed to find fully qualified type name for property ' + this.name + ' with type ' + this.type );
}

this.fullyQualifiedTypeName = result;
Object.defineProperty(this, 'fullyQualifiedTypeName', { enumerable: false });

const result = modelFile.getFullyQualifiedTypeName(this.type);
if(!result) {
throw new Error('Failed to find fully qualified type name for property ' + this.name + ' with type ' + this.type );
return result;
}

return result;
}

/**
Expand Down
50 changes: 50 additions & 0 deletions packages/composer-common/lib/readonlydecorator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

const Decorator = require('./introspect/decorator');
const IllegalModelException = require('./introspect/illegalmodelexception');

/**
* Specialised decorator implementation for the @readonly decorator.
*/
class ReadOnlyDecorator extends Decorator {

/**
* Create a Decorator.
* @param {ClassDeclaration | Property} parent - the owner of this property
* @param {Object} ast - The AST created by the parser
* @throws {IllegalModelException}
*/
constructor(parent, ast) {
super(parent, ast);
}

/**
* Process the AST and build the model
* @throws {IllegalModelException}
* @private
*/
process() {
super.process();
const args = this.getArguments();
if (args.length !== 0) {
throw new IllegalModelException(`@readonly decorator expects 0 arguments, but ${args.length} arguments were specified.`, this.parent.getModelFile(), this.ast.location);
}
}

}

module.exports = ReadOnlyDecorator;
42 changes: 42 additions & 0 deletions packages/composer-common/lib/readonlydecoratorfactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

const DecoratorFactory = require('./introspect/decoratorfactory');
const ReadOnlyDecorator = require('./readonlydecorator');

/**
* A decorator factory for the @readonly decorator.
*/
class ReadOnlyDecoratorFactory extends DecoratorFactory {

/**
* Process the decorator, and return a specific implementation class for that
* decorator, or return null if this decorator is not handled by this processor.
* @abstract
* @param {ClassDeclaration | Property} parent - the owner of this property
* @param {Object} ast - The AST created by the parser
* @return {Decorator} The decorator.
*/
newDecorator(parent, ast) {
if (ast.name !== 'readonly') {
return null;
}
return new ReadOnlyDecorator(parent, ast);
}

}

module.exports = ReadOnlyDecoratorFactory;
42 changes: 35 additions & 7 deletions packages/composer-common/lib/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ class Serializer {
this.defaultOptions = Object.assign({}, baseDefaultOptions, newDefaultOptions);
}


/**
* Generate validation parameters for a Resource
* @private
* @param {Resource} resource - The instance being worked
* @returns {Object} parameters used for validation
*/
validationParameters(resource){
const parameters = {};
parameters.stack = new TypedStack(resource);
parameters.modelManager = this.modelManager;
parameters.seenResources = new Set();
parameters.dedupeResources = new Set();
return parameters;
}

/**
* <p>
* Convert a {@link Resource} to a JavaScript object suitable for long-term
Expand All @@ -84,6 +100,9 @@ class Serializer {
* @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
* @param {boolean} [options.useOriginal] - shortcut the generation of the JSON structure from
* the resource and directly return the $original if present from the creation of the resource
* through the fromJSON method
* @return {Object} - The Javascript Object that represents the resource
* @throws {Error} - throws an exception if resource is not an instance of
* Resource or fails validation.
Expand All @@ -94,15 +113,18 @@ class Serializer {
throw new Error(Globalize.formatMessage('serializer-tojson-notcobject'));
}

const parameters = {};
parameters.stack = new TypedStack(resource);
parameters.modelManager = this.modelManager;
parameters.seenResources = new Set();
parameters.dedupeResources = new Set();
// Assign options
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;

// Enable shortcut retrieval of original JSON stored during serializer.fromJSON() method call
if (resource.$original && options.useOriginal) {
return resource.$original;
}

const parameters = this.validationParameters(resource);
const classDeclaration = this.modelManager.getType( resource.getFullyQualifiedType() );

// validate the resource against the model
options = options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
// conditionally validate the resource against the model
if(options.validate) {
const validator = new ResourceValidator(options);
classDeclaration.accept(validator, parameters);
Expand All @@ -120,6 +142,7 @@ class Serializer {
// this performs the conversion of the resouce into a standard JSON object
let result = classDeclaration.accept(generator, parameters);
return result;

}

/**
Expand Down Expand Up @@ -184,6 +207,11 @@ class Serializer {
resource.validate();
}

// Store the original JSON object to enable consditional retrieval later
delete jsonObject.$networkId;
resource.$original = jsonObject;
Object.defineProperty(resource, '$original', { enumerable: false });

return resource;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
const ClassDeclaration = require('../introspect/classdeclaration');
const EnumDeclaration = require('../introspect/enumdeclaration');
const Field = require('../introspect/field');
// const leftPad = require('left-pad');
const padStart = require('lodash.padstart');
const ModelUtil = require('../modelutil');
const RelationshipDeclaration = require('../introspect/relationshipdeclaration');
Expand Down
24 changes: 14 additions & 10 deletions packages/composer-common/lib/serializer/jsonpopulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,25 @@ function isSystemProperty(name) {
* @private
*/
function getAssignableProperties(resourceData) {
return Object.keys(resourceData).filter((property) => {
return !isSystemProperty(property) && !Util.isNull(resourceData[property]);
});
const assignable = [];
for (const property in resourceData){
if(!isSystemProperty(property) && !Util.isNull(resourceData[property])){
assignable.push(property);
}
}
return assignable;
}

/**
* Assert that all resource properties exist in a given class declaration.
* @param {Array} properties Property names.
* @param {Array} propertyNames Property names.
* @param {ClassDeclaration} classDeclaration class declaration.
* @throws {ValidationException} if any properties are not defined by the class declaration.
* @private
*/
function validateProperties(properties, classDeclaration) {
function validateProperties(propertyNames, classDeclaration) {
const expectedProperties = classDeclaration.getProperties().map((property) => property.getName());
const invalidProperties = properties.filter((property) => !expectedProperties.includes(property));
const invalidProperties = propertyNames.filter((property) => !expectedProperties.includes(property));
if (invalidProperties.length > 0) {
const errorText = `Unexpected properties for type ${classDeclaration.getFullyQualifiedName()}: ` +
invalidProperties.join(', ');
Expand Down Expand Up @@ -117,12 +121,12 @@ class JSONPopulator {
const properties = getAssignableProperties(jsonObj);
validateProperties(properties, classDeclaration);

properties.forEach((property) => {
for (const property of properties) {
const value = jsonObj[property];
parameters.jsonStack.push(value);
const classProperty = classDeclaration.getProperty(property);
resourceObj[property] = classProperty.accept(this,parameters);
});
resourceObj[property] = classProperty.accept(this, parameters);
}

return resourceObj;
}
Expand Down Expand Up @@ -194,7 +198,7 @@ class JSONPopulator {
classDeclaration.accept(this, parameters);
}
else {
result = this.convertToObject(field,jsonItem);
result = this.convertToObject(field, jsonItem);
}

return result;
Expand Down
1 change: 0 additions & 1 deletion packages/composer-common/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ class Util {
if (transaction instanceof Resource){
transaction.setIdentifier(txId.idStr);
json = serializer.toJSON(transaction);

} else {
transaction.transactionId = txId.idStr;
json=transaction;
Expand Down
13 changes: 13 additions & 0 deletions packages/composer-common/test/businessnetworkdefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const moxios = require('moxios');
const nodeUtil = require('util');
const os = require('os');
const path = require('path');
const ReadOnlyDecorator = require('../lib/readonlydecorator');
const ReturnsDecorator = require('../lib/returnsdecorator');
const rimraf = nodeUtil.promisify(require('rimraf'));

Expand Down Expand Up @@ -516,6 +517,18 @@ describe('BusinessNetworkDefinition', () => {
decorator.should.be.an.instanceOf(ReturnsDecorator);
});

it('should install the decorator processor for @readonly', () => {
const bnd = new BusinessNetworkDefinition('[email protected]', 'description', null, 'readme');
const modelManager = bnd.getModelManager();
modelManager.addModelFile(`
namespace org.acme
@readonly
transaction T{ }`);
const transactionDeclaration = modelManager.getType('org.acme.T');
const decorator = transactionDeclaration.getDecorator('readonly');
decorator.should.be.an.instanceOf(ReadOnlyDecorator);
});

});

});
Loading

0 comments on commit f2adfab

Please sign in to comment.