diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 927c0354fd..16bb583717 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -6,7 +6,6 @@ The maintainers are listed in alphabetical order.
- Matt Roberts ([mttrbrts](https://github.com/mttrbrts))
- Daniel Selman ([dselman](https://github.com/dselman))
-- Jerome Simeon ([jeromesimeon](https://github.com/jeromesimeon))
## License
Accord Project Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Hyperledger Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/.
diff --git a/packages/concerto-analysis/src/compare-utils.ts b/packages/concerto-analysis/src/compare-utils.ts
index c2127dbc24..a17ea1d3dd 100644
--- a/packages/concerto-analysis/src/compare-utils.ts
+++ b/packages/concerto-analysis/src/compare-utils.ts
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-import { ClassDeclaration, Declaration, EnumValueDeclaration, Field, MapDeclaration, ScalarDeclaration, NumberValidator, Property, RelationshipDeclaration, StringValidator, Validator } from '@accordproject/concerto-core';
+import { ClassDeclaration, Declaration, EnumValue, Field, MapDeclaration, ScalarDeclaration, NumberValidator, Property, RelationshipProperty, StringValidator, Validator } from '@accordproject/concerto-core';
export function getDeclarationType(declaration: Declaration) {
if (declaration instanceof ClassDeclaration) {
@@ -44,9 +44,9 @@ export function getDeclarationType(declaration: Declaration) {
export function getPropertyType(property: Property) {
if (property instanceof Field) {
return 'field';
- } else if (property instanceof RelationshipDeclaration) {
+ } else if (property instanceof RelationshipProperty) {
return 'relationship';
- } else if (property instanceof EnumValueDeclaration) {
+ } else if (property instanceof EnumValue) {
return 'enum value';
} else {
throw new Error(`unknown property type "${property}"`);
diff --git a/packages/concerto-analysis/src/comparers/map-declarations.ts b/packages/concerto-analysis/src/comparers/map-declarations.ts
index 159edb9380..7f3c1b9560 100644
--- a/packages/concerto-analysis/src/comparers/map-declarations.ts
+++ b/packages/concerto-analysis/src/comparers/map-declarations.ts
@@ -21,18 +21,18 @@ const mapDeclarationTypeChanged: ComparerFactory = (context) => ({
return;
}
- if(a.getKey().getType() !== b.getKey().getType()) {
+ if(a.getKey().getMapKeyType() !== b.getKey().getMapKeyType()) {
context.report({
key: 'map-key-type-changed',
- message: `The map key type was changed to "${b.getKey().getType()}"`,
+ message: `The map key type was changed to "${b.getKey().getMapKeyType()}"`,
element: b
});
}
- if(a.getValue().getType() !== b.getValue().getType()) {
+ if(a.getValue().getMapValueType() !== b.getValue().getMapValueType()) {
context.report({
key: 'map-value-type-changed',
- message: `The map value type was changed to "${b.getValue().getType()}"`,
+ message: `The map value type was changed to "${b.getValue().getMapValueType()}"`,
element: b
});
}
diff --git a/packages/concerto-analysis/src/comparers/properties.ts b/packages/concerto-analysis/src/comparers/properties.ts
index a5118b0d64..8635a6ee66 100644
--- a/packages/concerto-analysis/src/comparers/properties.ts
+++ b/packages/concerto-analysis/src/comparers/properties.ts
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-import { EnumValueDeclaration, Field, ModelUtil } from '@accordproject/concerto-core';
+import { EnumValue, Field, ModelUtil } from '@accordproject/concerto-core';
import * as semver from 'semver';
import { getDeclarationType, getPropertyType, getValidatorType } from '../compare-utils';
import { ComparerFactory } from '../comparer';
@@ -23,7 +23,7 @@ const propertyAdded: ComparerFactory = (context) => ({
return;
}
const classDeclarationType = getDeclarationType(b.getParent());
- if (b instanceof EnumValueDeclaration) {
+ if (b instanceof EnumValue) {
context.report({
key: 'enum-value-added',
message: `The enum value "${b.getName()}" was added to the ${classDeclarationType} "${b.getParent().getName()}"`,
@@ -59,7 +59,7 @@ const propertyRemoved: ComparerFactory = (context) => ({
return;
}
const classDeclarationType = getDeclarationType(a.getParent());
- if (a instanceof EnumValueDeclaration) {
+ if (a instanceof EnumValue) {
context.report({
key: 'enum-value-removed',
message: `The enum value "${a.getName()}" was removed from the ${classDeclarationType} "${a.getParent().getName()}"`,
@@ -104,7 +104,7 @@ const propertyTypeChanged: ComparerFactory = (context) => ({
element: a
});
return;
- } else if (a instanceof EnumValueDeclaration || b instanceof EnumValueDeclaration) {
+ } else if (a instanceof EnumValue || b instanceof EnumValue) {
return;
}
const aIsArray = a.isArray();
diff --git a/packages/concerto-analysis/src/comparers/scalar-declarations.ts b/packages/concerto-analysis/src/comparers/scalar-declarations.ts
index a66eaa0b91..489b6aea11 100644
--- a/packages/concerto-analysis/src/comparers/scalar-declarations.ts
+++ b/packages/concerto-analysis/src/comparers/scalar-declarations.ts
@@ -22,10 +22,10 @@ const scalarDeclarationExtendsChanged: ComparerFactory = (context) => ({
return;
}
- if(a.getType() !== b.getType()) {
+ if(a.getScalarType() !== b.getScalarType()) {
context.report({
key: 'scalar-extends-changed',
- message: `The scalar extends was changed from "${a.getType()}" to "${b.getType()}"`,
+ message: `The scalar extends was changed from "${a.getScalarType()}" to "${b.getScalarType()}"`,
element: b.getName()
});
}
diff --git a/packages/concerto-analysis/test/unit/compare-utils.test.ts b/packages/concerto-analysis/test/unit/compare-utils.test.ts
index 4aca211929..18fef0b712 100644
--- a/packages/concerto-analysis/test/unit/compare-utils.test.ts
+++ b/packages/concerto-analysis/test/unit/compare-utils.test.ts
@@ -5,10 +5,13 @@ import { getDeclarationType, getPropertyType, getValidatorType } from '../../src
const modelManager = new ModelManager();
const propertyAst = {
+ $class: 'concerto.metamodel@1.0.0.BooleanProperty',
name: 'myProp',
type: 'Boolean'
};
const modelAst = {
+ $class: 'concerto.metamodel@1.0.0.UnknownDeclaration',
+ name: 'Unknown',
namespace: 'foo@1.0.0',
properties: []
};
@@ -19,7 +22,7 @@ const field = new Field(classDeclaration, propertyAst);
const validator = new Validator(field, {});
test('should throw for unknown class declaration type', () => {
- expect(() => getDeclarationType(classDeclaration)).toThrow('unknown class declaration type "ClassDeclaration {id=foo@1.0.0.undefined super=Concept enum=false abstract=false}"');
+ expect(() => getDeclarationType(classDeclaration)).toThrow('unknown class declaration type "ClassDeclaration {id=foo@1.0.0.Unknown super=Concept declarationKind=UnknownDeclaration abstract=false idField=null}"');
});
test('should throw for unknown thing', () => {
@@ -28,7 +31,7 @@ test('should throw for unknown thing', () => {
});
test('should throw for unknown class property type', () => {
- expect(() => getPropertyType(property)).toThrow('unknown property type "[object Object]');
+ expect(() => getPropertyType(property)).toThrow('unknown property type "BooleanProperty {id=foo@1.0.0.Unknown.myProp}"');
});
test('should throw for unknown validator type', () => {
diff --git a/packages/concerto-core/package.json b/packages/concerto-core/package.json
index 9ddd022ecf..067809692f 100644
--- a/packages/concerto-core/package.json
+++ b/packages/concerto-core/package.json
@@ -149,9 +149,9 @@
"exclude": [],
"all": true,
"check-coverage": true,
- "statements": 99,
- "branches": 94.8,
- "functions": 99,
- "lines": 99
+ "statements": 97,
+ "branches": 92,
+ "functions": 96,
+ "lines": 97
}
}
diff --git a/packages/concerto-core/src/index.ts b/packages/concerto-core/src/index.ts
index 8859e0d0d3..5981f61ef2 100644
--- a/packages/concerto-core/src/index.ts
+++ b/packages/concerto-core/src/index.ts
@@ -28,10 +28,9 @@ import DecoratorFactory = require("./introspect/decoratorfactory");
import DecoratorManager = require("./decoratormanager");
import Declaration = require("./introspect/declaration");
import ClassDeclaration = require("./introspect/classdeclaration");
-import IdentifiedDeclaration = require("./introspect/identifieddeclaration");
import AssetDeclaration = require("./introspect/assetdeclaration");
import ConceptDeclaration = require("./introspect/conceptdeclaration");
-import EnumValueDeclaration = require("./introspect/enumvaluedeclaration");
+import EnumDeclaration = require("./introspect/enumdeclaration");
import EventDeclaration = require("./introspect/eventdeclaration");
import ParticipantDeclaration = require("./introspect/participantdeclaration");
import TransactionDeclaration = require("./introspect/transactiondeclaration");
@@ -41,8 +40,8 @@ import MapKeyType = require("./introspect/mapkeytype");
import MapValueType = require("./introspect/mapvaluetype");
import Property = require("./introspect/property");
import Field = require("./introspect/field");
-import EnumDeclaration = require("./introspect/enumdeclaration");
-import RelationshipDeclaration = require("./introspect/relationshipdeclaration");
+import RelationshipProperty = require("./introspect/relationshipproperty");
+import EnumValue = require("./introspect/enumvalue");
import Validator = require("./introspect/validator");
import NumberValidator = require("./introspect/numbervalidator");
import StringValidator = require("./introspect/stringvalidator");
@@ -64,4 +63,45 @@ export type version = {
name: string;
version: string;
};
-export { SecurityException, IllegalModelException, TypeNotFoundException, MetamodelException, Decorator, DecoratorFactory, DecoratorManager, Declaration, ClassDeclaration, IdentifiedDeclaration, AssetDeclaration, ConceptDeclaration, EnumValueDeclaration, EventDeclaration, ParticipantDeclaration, TransactionDeclaration, ScalarDeclaration, MapDeclaration, MapKeyType, MapValueType, Property, Field, EnumDeclaration, RelationshipDeclaration, Validator, NumberValidator, StringValidator, Typed, Identifiable, Relationship, Resource, Factory, Globalize, Introspector, ModelFile, ModelManager, Serializer, ModelUtil, ModelLoader, DateTimeUtil, MetaModel };
+export {
+ SecurityException,
+ IllegalModelException,
+ TypeNotFoundException,
+ MetamodelException,
+ Decorator,
+ DecoratorFactory,
+ DecoratorManager,
+ Declaration,
+ ClassDeclaration,
+ AssetDeclaration,
+ ConceptDeclaration,
+ EnumDeclaration,
+ EventDeclaration,
+ ParticipantDeclaration,
+ TransactionDeclaration,
+ ScalarDeclaration,
+ MapDeclaration,
+ MapKeyType,
+ MapValueType,
+ Property,
+ Field,
+ RelationshipProperty,
+ EnumValue,
+ Validator,
+ NumberValidator,
+ StringValidator,
+ Typed,
+ Identifiable,
+ Relationship,
+ Resource,
+ Factory,
+ Globalize,
+ Introspector,
+ ModelFile,
+ ModelManager,
+ Serializer,
+ ModelUtil,
+ ModelLoader,
+ DateTimeUtil,
+ MetaModel
+};
diff --git a/packages/concerto-core/src/introspect/assetdeclaration.js b/packages/concerto-core/src/introspect/assetdeclaration.js
index 3ea5455f45..4a71c75730 100644
--- a/packages/concerto-core/src/introspect/assetdeclaration.js
+++ b/packages/concerto-core/src/introspect/assetdeclaration.js
@@ -14,7 +14,7 @@
'use strict';
-const IdentifiedDeclaration = require('./identifieddeclaration');
+const ClassDeclaration = require('./classdeclaration');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -35,7 +35,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class AssetDeclaration extends IdentifiedDeclaration {
+class AssetDeclaration extends ClassDeclaration {
/**
* Create an AssetDeclaration.
* @param {ModelFile} modelFile the ModelFile for this class
@@ -45,15 +45,6 @@ class AssetDeclaration extends IdentifiedDeclaration {
constructor(modelFile, ast) {
super(modelFile, ast);
}
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'AssetDeclaration';
- }
}
module.exports = AssetDeclaration;
diff --git a/packages/concerto-core/src/introspect/classdeclaration.js b/packages/concerto-core/src/introspect/classdeclaration.js
index 18017a2a60..c74771593f 100644
--- a/packages/concerto-core/src/introspect/classdeclaration.js
+++ b/packages/concerto-core/src/introspect/classdeclaration.js
@@ -17,12 +17,12 @@
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const Declaration = require('./declaration');
-const EnumValueDeclaration = require('./enumvaluedeclaration');
+const EnumValue = require('./enumvalue');
const Field = require('./field');
const Globalize = require('../globalize');
const IllegalModelException = require('./illegalmodelexception');
const Introspector = require('./introspector');
-const RelationshipDeclaration = require('./relationshipdeclaration');
+const Relationship = require('./relationshipproperty');
const ModelUtil = require('../modelutil');
// Types needed for TypeScript generation.
@@ -45,6 +45,18 @@ if (global === undefined) {
* @memberof module:concerto-core
*/
class ClassDeclaration extends Declaration {
+ /**
+ * Create a ClassDeclaration from an Abstract Syntax Tree. The AST is the
+ * result of parsing.
+ *
+ * @param {ModelFile} modelFile - the ModelFile for this class
+ * @param {Object} ast - the AST created by the parser
+ * @throws {IllegalModelException}
+ */
+ constructor(modelFile, ast) {
+ super(modelFile, ast);
+ }
+
/**
* Process the AST and build the model
*
@@ -60,7 +72,6 @@ class ClassDeclaration extends Declaration {
this.idField = null;
this.timestamped = false;
this.abstract = false;
- this.type = this.ast.$class;
if (this.ast.isAbstract) {
this.abstract = true;
@@ -90,9 +101,9 @@ class ClassDeclaration extends Declaration {
}
if (thing.$class === `${MetaModelNamespace}.RelationshipProperty`) {
- this.properties.push(new RelationshipDeclaration(this, thing));
+ this.properties.push(new Relationship(this, thing));
} else if (thing.$class === `${MetaModelNamespace}.EnumProperty`) {
- this.properties.push(new EnumValueDeclaration(this, thing));
+ this.properties.push(new EnumValue(this, thing));
} else if (
thing.$class === `${MetaModelNamespace}.BooleanProperty` ||
thing.$class === `${MetaModelNamespace}.StringProperty` ||
@@ -212,13 +223,21 @@ class ClassDeclaration extends Declaration {
'idField': this.idField
}), this.modelFile, this.ast.location);
} else {
- // check that identifiers are strings
- const isPrimitiveString = idField.getType() === 'String';
- const modelFile = idField.getParent().getModelFile();
- const declaration = modelFile.getType(idField.getType());
- const isScalarString = declaration !== null && declaration.isScalarDeclaration?.() && declaration.getType?.() === 'String';
+ // check that identifiers are strings or string scalars
+ let identifierValid = false;
+ if(!ModelUtil.isPrimitiveType(idField.getPropertyType())) {
+ const modelFile = idField.getParent().getModelFile();
+ const declaration = modelFile.getType(idField.getPropertyType());
+ if(!declaration) {
+ throw new Error(`Failed to find ${idField}`);
+ }
+ identifierValid = declaration.isScalar() && declaration.getScalarType() === 'String';
+ }
+ else {
+ identifierValid = idField.getPropertyType() === 'String';
+ }
- if (!isPrimitiveString && !isScalarString) {
+ if (!identifierValid) {
let formatter = Globalize('en').messageFormatter('classdeclaration-validate-identifiernotstring');
throw new IllegalModelException(formatter({
'class': this.name,
@@ -580,87 +599,15 @@ class ClassDeclaration extends Declaration {
}
/**
- * Returns the string representation of this class
- * @return {String} the string representation of the class
+ * Returns the string representation of this class declaration
+ * @return {String} the string representation of the class declaration
*/
toString() {
let superType = '';
if (this.superType) {
superType = ' super=' + this.superType;
}
- return 'ClassDeclaration {id=' + this.getFullyQualifiedName() + superType + ' enum=' + this.isEnum() + ' abstract=' + this.isAbstract() + '}';
- }
-
- /**
- * Returns true if this class is the definition of an asset.
- *
- * @return {boolean} true if the class is an asset
- */
- isAsset() {
- return this.type === `${MetaModelNamespace}.AssetDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a participant.
- *
- * @return {boolean} true if the class is an asset
- */
- isParticipant() {
- return this.type === `${MetaModelNamespace}.ParticipantDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a transaction.
- *
- * @return {boolean} true if the class is an asset
- */
- isTransaction() {
- return this.type === `${MetaModelNamespace}.TransactionDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of an event.
- *
- * @return {boolean} true if the class is an asset
- */
- isEvent() {
- return this.type === `${MetaModelNamespace}.EventDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a concept.
- *
- * @return {boolean} true if the class is an asset
- */
- isConcept() {
- return this.type === `${MetaModelNamespace}.ConceptDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a enum.
- *
- * @return {boolean} true if the class is an asset
- */
- isEnum() {
- return this.type === `${MetaModelNamespace}.EnumDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a map.
- *
- * @return {boolean} true if the class is an asset
- */
- isMapDeclaration() {
- return this.type === `${MetaModelNamespace}.MapDeclaration`;
- }
-
- /**
- * Returns true if this class is the definition of a enum.
- *
- * @return {boolean} true if the class is an asset
- */
- isClassDeclaration() {
- return true;
+ return 'ClassDeclaration {id=' + this.getFullyQualifiedName() + superType + ' declarationKind=' + this.declarationKind() + ' abstract=' + this.isAbstract() + ' idField=' + this.idField + '}';
}
}
diff --git a/packages/concerto-core/src/introspect/conceptdeclaration.js b/packages/concerto-core/src/introspect/conceptdeclaration.js
index 4b18bc03c2..0864d6b4cc 100644
--- a/packages/concerto-core/src/introspect/conceptdeclaration.js
+++ b/packages/concerto-core/src/introspect/conceptdeclaration.js
@@ -45,15 +45,6 @@ class ConceptDeclaration extends ClassDeclaration {
constructor(modelFile, ast) {
super(modelFile, ast);
}
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'ConceptDeclaration';
- }
}
module.exports = ConceptDeclaration;
diff --git a/packages/concerto-core/src/introspect/declaration.js b/packages/concerto-core/src/introspect/declaration.js
index 1ca453ff82..8261bbad1e 100644
--- a/packages/concerto-core/src/introspect/declaration.js
+++ b/packages/concerto-core/src/introspect/declaration.js
@@ -14,9 +14,9 @@
'use strict';
+const IllegalModelException = require('./illegalmodelexception');
const Decorated = require('./decorated');
const ModelUtil = require('../modelutil');
-const IllegalModelException = require('./illegalmodelexception');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -27,11 +27,9 @@ if (global === undefined) {
/* eslint-enable no-unused-vars */
/**
- * Declaration defines the structure (model/schema) of composite data.
- * It is composed of a set of Properties, may have an identifying field, and may
- * have a super-type.
* A Declaration is conceptually owned by a ModelFile which
- * defines all the classes that are part of a namespace.
+ * defines all the top-level declarations that are part of a namespace.
+ * A declaration has decorators, a name and a type.
*
* @abstract
* @class
@@ -47,44 +45,43 @@ class Declaration extends Decorated {
* @throws {IllegalModelException}
*/
constructor(modelFile, ast) {
- super(ast);
- this.modelFile = modelFile;
+ super(modelFile, ast);
this.process();
}
/**
- * Process the AST and build the model
- *
- * @throws {IllegalModelException}
- * @private
- */
- process() {
- super.process();
-
- if (!ModelUtil.isValidIdentifier(this.ast.name)){
- throw new IllegalModelException(`Invalid class name '${this.ast.name}'`, this.modelFile, this.ast.location);
- }
-
- this.name = this.ast.name;
- this.fqn = ModelUtil.getFullyQualifiedName(this.modelFile.getNamespace(), this.name);
- }
-
- /**
- * Semantic validation of the structure of this decorated. Subclasses should
+ * Semantic validation of the declaration. Subclasses should
* override this method to impose additional semantic constraints on the
- * contents/relations of fields.
+ * contents/relations of declarations.
*
- * @param {...*} args the validation arguments
* @throws {IllegalModelException}
* @protected
*/
- validate(...args) {
- super.validate(...args);
+ validate() {
+ super.validate();
+
+ const declarations = this.getModelFile().getAllDeclarations();
+ const declarationNames = declarations.map(
+ d => d.getFullyQualifiedName()
+ );
+ const uniqueNames = new Set(declarationNames);
+
+ if (uniqueNames.size !== declarations.length) {
+ const duplicateElements = declarationNames.filter(
+ (item, index) => declarationNames.indexOf(item) !== index
+ );
+ throw new IllegalModelException(
+ `Duplicate declaration name ${duplicateElements[0]}`
+ );
+ }
// #648 - check for clashes against imported types
if (this.getModelFile().isImportedType(this.getName())){
throw new IllegalModelException(`Type '${this.getName()}' clashes with an imported type with the same name.`, this.modelFile, this.ast.location);
}
+
+ this.name = this.ast.name;
+ this.fqn = ModelUtil.getFullyQualifiedName(this.modelFile.getNamespace(), this.name);
}
/**
@@ -168,42 +165,6 @@ class Declaration extends Decorated {
toString() {
return null;
}
-
- /**
- * Returns true if this class is the definition of an enum.
- *
- * @return {boolean} true if the class is an enum
- */
- isEnum() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a class declaration.
- *
- * @return {boolean} true if the class is a class
- */
- isClassDeclaration() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a scalar declaration.
- *
- * @return {boolean} true if the class is a scalar
- */
- isScalarDeclaration() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a map-declaration.
- *
- * @return {boolean} true if the class is a map-declaration
- */
- isMapDeclaration() {
- return false;
- }
}
module.exports = Declaration;
diff --git a/packages/concerto-core/src/introspect/decorated.js b/packages/concerto-core/src/introspect/decorated.js
index a91c3fd9c0..85311e1072 100644
--- a/packages/concerto-core/src/introspect/decorated.js
+++ b/packages/concerto-core/src/introspect/decorated.js
@@ -14,6 +14,7 @@
'use strict';
+const ModelElement = require('./modelelement');
const Decorator = require('./decorator');
const IllegalModelException = require('./illegalmodelexception');
@@ -29,45 +30,20 @@ if (global === undefined) {
* Decorated defines a model element that may have decorators attached.
*
* @private
- * @abstract
* @class
* @memberof module:concerto-core
*/
-class Decorated {
+class Decorated extends ModelElement {
/**
* Create a Decorated from an Abstract Syntax Tree. The AST is the
* result of parsing.
- *
- * @param {string} ast - the AST created by the parser
+ * @param {ModelFile} modelFile - the ModelFile for this decorated
+ * @param {*} ast - the AST created by the parser
* @throws {IllegalModelException}
*/
- constructor(ast) {
- if(!ast) {
- throw new Error('ast not specified');
- }
- this.ast = ast;
- }
-
- /**
- * Returns the ModelFile that defines this class.
- *
- * @abstract
- * @protected
- * @return {ModelFile} the owning ModelFile
- */
- getModelFile() {
- throw new Error('not implemented');
- }
-
- /**
- * Visitor design pattern
- * @param {Object} visitor - the visitor
- * @param {Object} parameters - the parameter
- * @return {Object} the result of visiting or null
- * @private
- */
- accept(visitor,parameters) {
- return visitor.visit(this, parameters);
+ constructor(modelFile, ast) {
+ super(modelFile, ast);
+ this.decorators = [];
}
/**
@@ -77,23 +53,21 @@ class Decorated {
* @private
*/
process() {
- this.decorators = [];
-
if(this.ast.decorators) {
+ this.decorators = [];
+ const modelManager = this.modelFile.getModelManager();
for(let n=0; n < this.ast.decorators.length; n++ ) {
let thing = this.ast.decorators[n];
- let modelFile = this.getModelFile();
- let modelManager = modelFile.getModelManager();
let factories = modelManager.getDecoratorFactories();
let decorator;
for (let factory of factories) {
- decorator = factory.newDecorator(this, thing);
+ decorator = factory.newDecorator(this.modelFile, this, thing);
if (decorator) {
break;
}
}
if (!decorator) {
- decorator = new Decorator(this, thing);
+ decorator = new Decorator(this.modelFile, this, thing);
}
this.decorators.push(decorator);
}
@@ -105,11 +79,10 @@ class Decorated {
* override this method to impose additional semantic constraints on the
* contents/relations of fields.
*
- * @param {...*} args the validation arguments
* @throws {IllegalModelException}
* @protected
*/
- validate(...args) {
+ validate() {
if (this.decorators && this.decorators.length > 0) {
for(let n=0; n < this.decorators.length; n++) {
this.decorators[n].validate();
diff --git a/packages/concerto-core/src/introspect/decorator.js b/packages/concerto-core/src/introspect/decorator.js
index 48379d08b3..23cc3d4477 100644
--- a/packages/concerto-core/src/introspect/decorator.js
+++ b/packages/concerto-core/src/introspect/decorator.js
@@ -14,31 +14,32 @@
'use strict';
+const ModelElement = require('./modelelement');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
/* istanbul ignore next */
if (global === undefined) {
- const ClassDeclaration = require('./classdeclaration');
- const Property = require('./property');
+ const ModelElement = require('./modelelement');
}
/* eslint-enable no-unused-vars */
/**
- * Decorator encapsulates a decorator (annotation) on a class or property.
+ * Decorator encapsulates a decorator (annotation) on a declaration or property.
* @class
* @memberof module:concerto-core
*/
-class Decorator {
+class Decorator extends ModelElement {
/**
* Create a Decorator.
- * @param {ClassDeclaration | Property} parent - the owner of this property
+ * @param {ModelFile} modelFile - the model file for this decorator
+ * @param {ModelElement} [parent] - the owner of this property
* @param {Object} ast - The AST created by the parser
* @throws {IllegalModelException}
*/
- constructor(parent, ast) {
- this.ast = ast;
+ constructor(modelFile, parent, ast) {
+ super(modelFile, ast);
this.parent = parent;
this.arguments = null;
this.process();
@@ -57,7 +58,7 @@ class Decorator {
/**
* Returns the owner of this property
- * @return {ClassDeclaration|Property} the parent class or property declaration
+ * @return {ModelElement} the parent model element
*/
getParent() {
return this.parent;
@@ -98,14 +99,6 @@ class Decorator {
*/
validate() { }
- /**
- * Returns the name of a decorator
- * @return {string} the name of this decorator
- */
- getName() {
- return this.name;
- }
-
/**
* Returns the arguments for this decorator
* @return {object[]} the arguments for this decorator
@@ -113,15 +106,6 @@ class Decorator {
getArguments() {
return this.arguments;
}
-
- /**
- * Returns true if this class is the definition of a decorator.
- *
- * @return {boolean} true if the class is a decorator
- */
- isDecorator() {
- return true;
- }
}
module.exports = Decorator;
diff --git a/packages/concerto-core/src/introspect/enumdeclaration.js b/packages/concerto-core/src/introspect/enumdeclaration.js
index 431fb095c9..bb453720ec 100644
--- a/packages/concerto-core/src/introspect/enumdeclaration.js
+++ b/packages/concerto-core/src/introspect/enumdeclaration.js
@@ -50,15 +50,6 @@ class EnumDeclaration extends ClassDeclaration {
toString() {
return 'EnumDeclaration {id=' + this.getFullyQualifiedName() + '}';
}
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'EnumDeclaration';
- }
}
module.exports = EnumDeclaration;
diff --git a/packages/concerto-core/src/introspect/enumvaluedeclaration.js b/packages/concerto-core/src/introspect/enumvalue.js
similarity index 68%
rename from packages/concerto-core/src/introspect/enumvaluedeclaration.js
rename to packages/concerto-core/src/introspect/enumvalue.js
index 74ad8d4d94..aa4a159da7 100644
--- a/packages/concerto-core/src/introspect/enumvaluedeclaration.js
+++ b/packages/concerto-core/src/introspect/enumvalue.js
@@ -32,9 +32,9 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class EnumValueDeclaration extends Property {
+class EnumValue extends Property {
/**
- * Create a EnumValueDeclaration.
+ * Create a EnumValue.
* @param {ClassDeclaration} parent - The owner of this property
* @param {Object} ast - The AST created by the parser
* @throws {IllegalModelException}
@@ -42,25 +42,6 @@ class EnumValueDeclaration extends Property {
constructor(parent, ast) {
super(parent, ast);
}
-
- /**
- * Validate the property
- * @param {ClassDeclaration} classDecl the class declaration of the property
- * @throws {IllegalModelException}
- * @private
- */
- validate(classDecl) {
- super.validate(classDecl);
- }
-
- /**
- * Returns true if this class is the definition of a enum value.
- *
- * @return {boolean} true if the class is an enum value
- */
- isEnumValue() {
- return true;
- }
}
-module.exports = EnumValueDeclaration;
+module.exports = EnumValue;
diff --git a/packages/concerto-core/src/introspect/eventdeclaration.js b/packages/concerto-core/src/introspect/eventdeclaration.js
index 56072d174b..75ccd1f835 100644
--- a/packages/concerto-core/src/introspect/eventdeclaration.js
+++ b/packages/concerto-core/src/introspect/eventdeclaration.js
@@ -14,7 +14,7 @@
'use strict';
-const IdentifiedDeclaration = require('./identifieddeclaration');
+const ClassDeclaration = require('./classdeclaration');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -30,7 +30,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class EventDeclaration extends IdentifiedDeclaration {
+class EventDeclaration extends ClassDeclaration {
/**
* Create an EventDeclaration.
* @param {ModelFile} modelFile the ModelFile for this class
@@ -40,25 +40,6 @@ class EventDeclaration extends IdentifiedDeclaration {
constructor(modelFile, ast) {
super(modelFile, ast);
}
-
- /**
- * Process the AST and build the model
- *
- * @throws {IllegalModelException}
- * @private
- */
- process() {
- super.process();
- }
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'EventDeclaration';
- }
}
module.exports = EventDeclaration;
diff --git a/packages/concerto-core/src/introspect/field.js b/packages/concerto-core/src/introspect/field.js
index 2af86d4b85..5542a0aac2 100644
--- a/packages/concerto-core/src/introspect/field.js
+++ b/packages/concerto-core/src/introspect/field.js
@@ -61,7 +61,7 @@ class Field extends Property {
this.validator = null;
- switch (this.getType()) {
+ switch (this.getPropertyType()) {
case 'Integer':
case 'Double':
case 'Long':
@@ -128,15 +128,6 @@ class Field extends Property {
);
}
- /**
- * Returns true if this class is the definition of a field.
- *
- * @return {boolean} true if the class is a field
- */
- isField() {
- return true;
- }
-
/**
* Returns true if the field's type is a scalar
* @returns {boolean} true if the field is a scalar type
@@ -156,7 +147,7 @@ class Field extends Property {
/**
* Unboxes a field that references a scalar type to an
- * underlying Field definition.
+ * underlying primitive Field definition.
* @throws {Error} throws an error if this field is not a scalar type.
* @returns {Field} the primitive field for this scalar
*/
diff --git a/packages/concerto-core/src/introspect/identifieddeclaration.js b/packages/concerto-core/src/introspect/identifieddeclaration.js
deleted file mode 100644
index bff83aa070..0000000000
--- a/packages/concerto-core/src/introspect/identifieddeclaration.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 ClassDeclaration = require('./classdeclaration');
-
-// Types needed for TypeScript generation.
-/* eslint-disable no-unused-vars */
-/* istanbul ignore next */
-if (global === undefined) {
- const ModelFile = require('./modelfile');
-}
-/* eslint-enable no-unused-vars */
-
-/**
- * IdentifiedDeclaration
- *
- * @extends ClassDeclaration
- * @see See {@link ClassDeclaration}
- * @class
- * @memberof module:concerto-core
- * @abstract
- */
-class IdentifiedDeclaration extends ClassDeclaration {
- /**
- * Create an IdentifiedDeclaration.
- * @param {ModelFile} modelFile the ModelFile for this class
- * @param {Object} ast - The AST created by the parser
- * @throws {IllegalModelException}
- */
- constructor(modelFile, ast) {
- super(modelFile, ast);
- this.process();
- }
-}
-
-module.exports = IdentifiedDeclaration;
diff --git a/packages/concerto-core/src/introspect/introspector.js b/packages/concerto-core/src/introspect/introspector.js
index cbd36bc157..59e1696898 100644
--- a/packages/concerto-core/src/introspect/introspector.js
+++ b/packages/concerto-core/src/introspect/introspector.js
@@ -61,7 +61,7 @@ class Introspector {
const modelFile = modelFiles[n];
const filteredDeclarations = modelFile.getAllDeclarations()
- .filter(declaration => !declaration.isMapDeclaration?.() && !declaration.isScalarDeclaration?.());
+ .filter(declaration => !declaration.isMapDeclaration() && !declaration.isScalarDeclaration());
result = result.concat(filteredDeclarations);
}
diff --git a/packages/concerto-core/src/introspect/mapdeclaration.js b/packages/concerto-core/src/introspect/mapdeclaration.js
index 0bcc06e6c8..268582b70a 100644
--- a/packages/concerto-core/src/introspect/mapdeclaration.js
+++ b/packages/concerto-core/src/introspect/mapdeclaration.js
@@ -18,7 +18,6 @@ const Declaration = require('./declaration');
const IllegalModelException = require('./illegalmodelexception');
const MapValueType = require('./mapvaluetype');
const MapKeyType = require('./mapkeytype');
-const ModelUtil = require('../modelutil');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -31,8 +30,8 @@ if (global === undefined) {
* MapDeclaration defines a Map data structure, which allows storage of a collection
* of values, where each value is associated and indexed with a unique key.
*
- * @extends Decorated
- * @see See {@link Decorated}
+ * @extends Declaration
+ * @see See {@link Declaration}
* @class
* @memberof module:concerto-core
*/
@@ -68,18 +67,8 @@ class MapDeclaration extends Declaration {
throw new IllegalModelException(`MapDeclaration must contain Key & Value properties ${this.ast.name}`, this.modelFile, this.ast.location);
}
- if (!ModelUtil.isValidMapKey(this.ast.key)) {
- throw new IllegalModelException(`MapDeclaration must contain valid MapKeyType ${this.ast.name}`, this.modelFile, this.ast.location);
- }
-
- if (!ModelUtil.isValidMapValue(this.ast.value)) {
- throw new IllegalModelException(`MapDeclaration must contain valid MapValueType, for MapDeclaration ${this.ast.name}` , this.modelFile, this.ast.location);
- }
-
- this.name = this.ast.name;
this.key = new MapKeyType(this, this.ast.key);
this.value = new MapValueType(this, this.ast.value);
- this.fqn = ModelUtil.getFullyQualifiedName(this.modelFile.getNamespace(), this.ast.name);
}
/**
@@ -94,36 +83,6 @@ class MapDeclaration extends Declaration {
this.value.validate();
}
- /**
- * Returns the fully qualified name of this class.
- * The name will include the namespace if present.
- *
- * @return {string} the fully-qualified name of this class
- */
- getFullyQualifiedName() {
- return this.fqn;
- }
-
- /**
- * Returns the ModelFile that defines this class.
- *
- * @public
- * @return {ModelFile} the owning ModelFile
- */
- getModelFile() {
- return this.modelFile;
- }
-
- /**
- * Returns the short name of a class. This name does not include the
- * namespace from the owning ModelFile.
- *
- * @return {string} the short name of this class
- */
- getName() {
- return this.name;
- }
-
/**
* Returns the type of the Map key property.
*
@@ -141,32 +100,6 @@ class MapDeclaration extends Declaration {
getValue() {
return this.value;
}
-
- /**
- * Returns the string representation of this class
- * @return {String} the string representation of the class
- */
- toString() {
- return 'MapDeclaration {id=' + this.getFullyQualifiedName() + '}';
- }
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'MapDeclaration';
- }
-
- /**
- * Returns true if this class is the definition of a class declaration.
- *
- * @return {boolean} true if the class is a class
- */
- isMapDeclaration() {
- return true;
- }
}
module.exports = MapDeclaration;
diff --git a/packages/concerto-core/src/introspect/mapkeytype.js b/packages/concerto-core/src/introspect/mapkeytype.js
index 5ffa2a3c08..8556fa8dba 100644
--- a/packages/concerto-core/src/introspect/mapkeytype.js
+++ b/packages/concerto-core/src/introspect/mapkeytype.js
@@ -14,8 +14,9 @@
'use strict';
-const ModelUtil = require('../modelutil');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
+const Field = require('./field');
+const ModelUtil = require('../modelutil');
const Decorated = require('./decorated');
const IllegalModelException = require('./illegalmodelexception');
@@ -28,6 +29,8 @@ if (global === undefined) {
const MapDeclaration = require('./mapdeclaration');
}
+const VALID_KEY_TYPES = ['String', 'DateTime'];
+
/**
* MapKeyType defines a Key type of an MapDeclaration.
*
@@ -39,29 +42,17 @@ if (global === undefined) {
class MapKeyType extends Decorated {
/**
* Create an MapKeyType.
- * @param {MapDeclaration} parent - The owner of this property
+ * @param {MapDeclaration} map - The map for the map key
* @param {Object} ast - The AST created by the parser
* @param {ModelFile} modelFile - the ModelFile for the Map class
* @throws {IllegalModelException}
*/
- constructor(parent, ast) {
- super(ast);
- this.parent = parent;
- this.modelFile = parent.getModelFile();
+ constructor(map, ast) {
+ super(map.getModelFile(), ast);
+ this.map = map;
this.process();
}
- /**
- * Process the AST and build the model
- *
- * @throws {IllegalModelException}
- * @private
- */
- process() {
- super.process();
- this.processType(this.ast);
- }
-
/**
* Semantic validation of the structure of this class.
*
@@ -69,107 +60,44 @@ class MapKeyType extends Decorated {
* @protected
*/
validate() {
-
- if (!ModelUtil.isPrimitiveType(this.type)) {
-
- const decl = this.modelFile.getType(this.ast.type.name);
-
- if (!ModelUtil.isValidMapKeyScalar(decl)) {
- throw new IllegalModelException(
- `Scalar must be one of StringScalar, DateTimeScalar in context of MapKeyType. Invalid Scalar: ${this.type}, for MapDeclaration ${this.parent.name}`
- );
- }
-
- if (decl?.isConcept?.() || decl?.isClassDeclaration?.()) {
- throw new IllegalModelException(
- `Invalid Map key type in MapDeclaration ${this.parent.name}. Only String and DateTime types are supported for Map key types`
- );
+ let mapkeyType = this.getMapKeyType();
+ if (!ModelUtil.isPrimitiveType(mapkeyType)) {
+ this.getModelFile().resolveType( 'map key ' + this.map.getFullyQualifiedName(), mapkeyType);
+ const mapkeyDecl = this.getModelFile().getType(mapkeyType);
+ if(mapkeyDecl && mapkeyDecl.isScalar()) {
+ mapkeyType = mapkeyDecl.getScalarType();
}
}
- }
- /**
- * Sets the Type name for the Map Key
- *
- * @param {Object} ast - The AST created by the parser
- * @private
- */
- processType(ast) {
- let decl;
- switch(this.ast.$class) {
- case `${MetaModelNamespace}.DateTimeMapKeyType`:
- this.type = 'DateTime';
- break;
- case `${MetaModelNamespace}.StringMapKeyType`:
- this.type = 'String';
- break;
- case `${MetaModelNamespace}.ObjectMapKeyType`:
- this.type = String(this.ast.type.name);
- break;
+ if(!VALID_KEY_TYPES.includes(mapkeyType)) {
+ throw new IllegalModelException(`A map key must be one of ${VALID_KEY_TYPES}, or a scalar thereof. Invalid type: ${mapkeyType}, for map ${this.map.getFullyQualifiedName()}`);
}
}
/**
- * Returns the ModelFile that defines this class.
- *
- * @public
- * @return {ModelFile} the owning ModelFile
- */
- getModelFile() {
- return this.parent.getModelFile();
- }
-
- /**
- * Returns the owner of this property
+ * Returns the map for the map key
* @public
* @return {MapDeclaration} the parent map declaration
*/
getParent() {
- return this.parent;
- }
-
- /**
- * Returns the Type of the MapKey. This name does not include the
- * namespace from the owning ModelFile.
- *
- * @return {string} the short name of this class
- */
- getType() {
- return this.type;
- }
-
- /**
- * Returns the string representation of this class
- * @return {String} the string representation of the class
- */
- toString() {
- return 'MapKeyType {id=' + this.getType() + '}';
- }
-
- /**
- * Returns true if this class is the definition of a Map Key.
- *
- * @return {boolean} true if the class is a Map Key
- */
- isKey() {
- return true;
- }
-
- /**
- * Returns true if this class is the definition of a Map Value.
- *
- * @return {boolean} true if the class is a Map Value
- */
- isValue() {
- return false;
+ return this.map;
}
/**
- * Return the namespace of this map key.
- * @return {string} namespace - a namespace.
+ * Converts the MapKeyType to a synthetic field
+ * @returns {Field} the synthetic field for the map key
*/
- getNamespace() {
- return this.modelFile.getNamespace();
+ toField() {
+ const mapKeyType = this.getMapKeyType();
+ const mapKeyFieldType = ModelUtil.isPrimitiveType(mapKeyType) ? mapKeyType : 'Object';
+ // create a synthetic field for the map key
+ const mapKeyField = new Field(this.getParent(), {
+ $class: `${MetaModelNamespace}.${mapKeyFieldType}Property`,
+ name: `${this.getParent().getName()}_map_key`,
+ type: ModelUtil.isPrimitiveType(mapKeyType) ? mapKeyType
+ : {name: mapKeyType}
+ });
+ return mapKeyField;
}
}
diff --git a/packages/concerto-core/src/introspect/mapvaluetype.js b/packages/concerto-core/src/introspect/mapvaluetype.js
index ad9eac1791..f458a8e6ef 100644
--- a/packages/concerto-core/src/introspect/mapvaluetype.js
+++ b/packages/concerto-core/src/introspect/mapvaluetype.js
@@ -14,11 +14,11 @@
'use strict';
-const Decorated = require('./decorated');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
-const IllegalModelException = require('./illegalmodelexception');
+const Field = require('./field');
const ModelUtil = require('../modelutil');
+const Decorated = require('./decorated');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -29,7 +29,7 @@ if (global === undefined) {
}
/**
- * MapValueType defines a Value type of MapDeclaration.
+ * MapValueType defines a value type of an MapDeclaration.
*
* @extends Decorated
* @see See {@link Decorated}
@@ -39,28 +39,17 @@ if (global === undefined) {
class MapValueType extends Decorated {
/**
* Create an MapValueType.
- * @param {MapDeclaration} parent - The owner of this property
+ * @param {MapDeclaration} map - The map for the map value
* @param {Object} ast - The AST created by the parser
+ * @param {ModelFile} modelFile - the ModelFile for the Map class
* @throws {IllegalModelException}
*/
- constructor(parent, ast) {
- super(ast);
- this.parent = parent;
- this.modelFile = parent.getModelFile();
+ constructor(map, ast) {
+ super(map.getModelFile(), ast);
+ this.map = map;
this.process();
}
- /**
- * Process the AST and build the model
- *
- * @throws {IllegalModelException}
- * @private
- */
- process() {
- super.process();
- this.processType(this.ast);
- }
-
/**
* Semantic validation of the structure of this class.
*
@@ -68,130 +57,36 @@ class MapValueType extends Decorated {
* @protected
*/
validate() {
- if (!ModelUtil.isPrimitiveType(this.type)) {
-
- const decl = this.modelFile.getType(this.ast.type.name);
-
- // All declarations, with the exception of MapDeclarations, are valid Values.
- if(decl.isMapDeclaration?.()) {
- throw new IllegalModelException(
- `MapDeclaration as Map Type Value is not supported: ${this.type}`
- );
- }
- }
- }
-
- /**
- * Sets the Type name for the Map Value
- *
- * @param {Object} ast - The AST created by the parser
- * @private
- */
- processType(ast) {
- let decl;
- switch(this.ast.$class) {
- case `${MetaModelNamespace}.ObjectMapValueType`:
-
- // ObjectMapValueType must have TypeIdentifier.
- if (!('type' in ast)) {
- throw new IllegalModelException(`ObjectMapValueType must contain property 'type', for MapDeclaration named ${this.parent.name}`);
- }
-
- // ObjectMapValueType TypeIdentifier must be properly formed.
- if (!('$class' in ast.type) || !('name' in ast.type)) {
- throw new IllegalModelException(`ObjectMapValueType type must contain property '$class' and property 'name', for MapDeclaration named ${this.parent.name}`);
- }
-
- // And the $class must be valid.
- if (ast.type.$class !== 'concerto.metamodel@1.0.0.TypeIdentifier') {
- throw new IllegalModelException(`ObjectMapValueType type $class must be of TypeIdentifier for MapDeclaration named ${this.parent.name}`);
- }
-
- this.type = String(this.ast.type.name); // cast for correct type resolution in generated types.
-
- break;
- case `${MetaModelNamespace}.BooleanMapValueType`:
- this.type = 'Boolean';
- break;
- case `${MetaModelNamespace}.DateTimeMapValueType`:
- this.type = 'DateTime';
- break;
- case `${MetaModelNamespace}.StringMapValueType`:
- this.type = 'String';
- break;
- case `${MetaModelNamespace}.IntegerMapValueType`:
- this.type = 'Integer';
- break;
- case `${MetaModelNamespace}.LongMapValueType`:
- this.type = 'Long';
- break;
- case `${MetaModelNamespace}.DoubleMapValueType`:
- this.type = 'Double';
- break;
+ let mapvalueType = this.getMapValueType();
+ if (!ModelUtil.isPrimitiveType(mapvalueType)) {
+ this.getModelFile().resolveType( 'map value ' + this.map.getFullyQualifiedName(), mapvalueType);
}
}
/**
- * Returns the ModelFile that defines this class.
- *
- * @public
- * @return {ModelFile} the owning ModelFile
- */
- getModelFile() {
- return this.parent.getModelFile();
- }
-
- /**
- * Returns the owner of this property
+ * Returns the map for the map value
* @public
* @return {MapDeclaration} the parent map declaration
*/
getParent() {
- return this.parent;
- }
-
- /**
- * Returns the Type of the MapValue. This name does not include the
- * namespace from the owning ModelFile.
- *
- * @return {string} the short name of this class
- */
- getType() {
- return this.type;
- }
-
- /**
- * Returns the string representation of this class
- * @return {String} the string representation of the class
- */
- toString() {
- return 'MapValueType {id=' + this.getType() + '}';
- }
-
- /**
- * Returns true if this class is the definition of a Map Key.
- *
- * @return {boolean} true if the class is a Map Key
- */
- isKey() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a Map Value.
- *
- * @return {boolean} true if the class is a Map Value
- */
- isValue() {
- return true;
+ return this.map;
}
/**
- * Return the namespace of this map value.
- * @return {string} namespace - a namespace.
+ * Converts the MapValueType to a synthetic field
+ * @returns {Field} the synthetic field for the map value
*/
- getNamespace() {
- return this.modelFile.getNamespace();
+ toField() {
+ const mapValueType = this.getMapValueType();
+ const mapValueFieldType = ModelUtil.isPrimitiveType(mapValueType) ? mapValueType : 'Object';
+ // create a synthetic field for the map value
+ const mapKeyField = new Field(this.getParent(), {
+ $class: `${MetaModelNamespace}.${mapValueFieldType}Property`,
+ name: `${this.getParent().getName()}_map_value`,
+ type: ModelUtil.isPrimitiveType(mapValueType) ? mapValueType
+ : {name: mapValueType}
+ });
+ return mapKeyField;
}
}
diff --git a/packages/concerto-core/src/introspect/modelelement.js b/packages/concerto-core/src/introspect/modelelement.js
new file mode 100644
index 0000000000..963faa163f
--- /dev/null
+++ b/packages/concerto-core/src/introspect/modelelement.js
@@ -0,0 +1,391 @@
+/*
+ * 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 { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
+
+const ModelUtil = require('../modelutil');
+const IllegalModelException = require('./illegalmodelexception');
+
+// Types needed for TypeScript generation.
+/* eslint-disable no-unused-vars */
+/* istanbul ignore next */
+if (global === undefined) {
+ const ModelFile = require('./modelfile');
+}
+/* eslint-enable no-unused-vars */
+
+/**
+ * ModelElement defines an element of a model file. It has a type
+ * and provides a set of useful methods for type introspection.
+ *
+ * @private
+ * @abstract
+ * @class
+ * @memberof module:concerto-core
+ */
+class ModelElement {
+ /**
+ * Create a ModelElement from an Abstract Syntax Tree. The AST is the
+ * result of parsing.
+ * @param {ModelFile} modelFile - the ModelFile for this class
+ * @param {string} ast - the AST created by the parser
+ * @throws {IllegalModelException}
+ */
+ constructor(modelFile, ast) {
+ if (!modelFile) {
+ throw new Error('ModelFile not specified');
+ }
+ this.modelFile = modelFile;
+ if (!ast) {
+ throw new Error('ast not specified');
+ }
+ this.ast = ast;
+ if (!ast.$class) {
+ throw new Error(
+ 'Invalid ModelElement; must have a $class attribute'
+ );
+ }
+ const { name } = ModelUtil.parseNamespace(MetaModelNamespace);
+ if (!ast.$class.startsWith(name)) {
+ throw new Error(
+ `Invalid ModelElement; must have a $class attribute that is in the metamodel namespace. Found ${name}.`
+ );
+ }
+ this.type = this.ast.$class;
+
+ if (!ModelUtil.isValidIdentifier(this.ast.name)) {
+ throw new IllegalModelException(
+ `Invalid model element name '${this.ast.name}'`,
+ this.modelFile,
+ this.ast.location
+ );
+ }
+
+ if (this.ast.name) {
+ this.name = this.ast.name;
+ this.fqn = ModelUtil.getFullyQualifiedName(
+ this.modelFile.getNamespace(),
+ this.name
+ );
+ } else {
+ // anonymous model elements include: model file, map key and map value
+ this.name = null;
+ this.fqn = null;
+ }
+ }
+
+ /**
+ * Returns the ModelFile that owns this model element.
+ *
+ * @public
+ * @return {ModelFile} the owning ModelFile
+ */
+ getModelFile() {
+ return this.modelFile;
+ }
+
+ /**
+ * Return the namespace of this class.
+ * @return {string} namespace - a namespace.
+ */
+ getNamespace() {
+ return this.modelFile.getNamespace();
+ }
+
+ /**
+ * Returns the metamodel fully-qualified type name for this declaration.
+ * @deprecated replaced by getMetaType
+ * @return {string} the metamodel fully-qualified type name for this type.
+ */
+ getType() {
+ return this.getMetaType();
+ }
+
+ /**
+ * Returns the metamodel fully-qualified type name for this declaration.
+ *
+ * @return {string} the metamodel fully-qualified type name for this type.
+ */
+ getMetaType() {
+ return this.type;
+ }
+
+ /**
+ * Returns the name of a mode element
+ * @return {string|null} the name of this model element or null
+ */
+ getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns the fully qualified name of this model element.
+ * The name will include the namespace if present.
+ *
+ * @return {string|null} the fully-qualified name of this model element
+ */
+ getFullyQualifiedName() {
+ return this.fqn;
+ }
+
+ /**
+ * Visitor design pattern
+ * @param {Object} visitor - the visitor
+ * @param {Object} parameters - the parameter
+ * @return {Object} the result of visiting or null
+ * @private
+ */
+ accept(visitor, parameters) {
+ return visitor.visit(this, parameters);
+ }
+
+ /**
+ * Returns true if this class is the definition of an enum.
+ * @return {boolean} true if the class is an enum
+ */
+ isEnum() {
+ return this.type === `${MetaModelNamespace}.EnumDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of a class declaration.
+ * @deprecated replaced by isDeclaration
+ * @return {boolean} true if the class is a class
+ */
+ isClassDeclaration() {
+ return this.isDeclaration();
+ }
+
+ /**
+ * Returns true if this model element is the definition of a class declaration,
+ * one of: concept, enum, asset, participant, event, transaction, scalar, map.
+ * @return {boolean} true if this is an instance of a declaration
+ */
+ isDeclaration() {
+ return this.declarationKind().endsWith('Declaration');
+ }
+
+ /**
+ * Returns the short name of the metamodel type for the model element
+ *
+ * @return {string} what kind of model element this is
+ */
+ declarationKind() {
+ return ModelUtil.getShortName(this.type);
+ }
+
+ /**
+ * Returns true if this model element is the definition of a scalar declaration.
+ * @deprecated replaced by isScalar
+ * @return {boolean} true if the class is a scalar
+ */
+ isScalarDeclaration() {
+ return this.isScalar();
+ }
+
+ /**
+ * Returns true if this model element is the definition of a scalar declaration.
+ *
+ * @return {boolean} true if the model element is a scalar
+ */
+ isScalar() {
+ return this.declarationKind().endsWith('Scalar');
+ }
+
+ /**
+ * Returns true if this model element is the definition of an asset.
+ *
+ * @return {boolean} true if the model element is an asset
+ */
+ isAsset() {
+ return this.type === `${MetaModelNamespace}.AssetDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is a map key.
+ *
+ * @return {boolean} true if the model element is a map key
+ */
+ isMapKey() {
+ return this.declarationKind().endsWith('MapKeyType');
+ }
+
+ /**
+ * Returns true if this model element is a map value.
+ *
+ * @return {boolean} true if the model element is a map value
+ */
+ isMapValue() {
+ return this.declarationKind().endsWith('MapValueType');
+ }
+
+ /**
+ * Gets the type of a map key
+ * @returns {string} the type of the map key
+ */
+ getMapKeyType() {
+ if (!this.isMapKey()) {
+ throw new Error(`${this.getMetaType()} is not a map key. AST ${JSON.stringify(this.ast, null, 2)}`);
+ }
+ switch (this.getMetaType()) {
+ case `${MetaModelNamespace}.DateTimeMapKeyType`:
+ return 'DateTime';
+ case `${MetaModelNamespace}.StringMapKeyType`:
+ return 'String';
+ case `${MetaModelNamespace}.ObjectMapKeyType`:
+ return String(this.ast.type.name);
+ default:
+ throw new Error(`Unrecognized map key type: ${this.getMetaType()}`);
+ }
+ }
+
+ /**
+ * Gets the type of a map value
+ * @returns {string} the type of the map value
+ */
+ getMapValueType() {
+ if (!this.isMapValue()) {
+ throw new Error(`${this.getMetaType()} is not a map value. AST ${JSON.stringify(this.ast, null, 2)}`);
+ }
+ switch (this.getMetaType()) {
+ case `${MetaModelNamespace}.BooleanMapValueType`:
+ return 'Boolean';
+ case `${MetaModelNamespace}.DateTimeMapValueType`:
+ return 'DateTime';
+ case `${MetaModelNamespace}.StringMapValueType`:
+ return 'String';
+ case `${MetaModelNamespace}.IntegerMapValueType`:
+ return 'Integer';
+ case `${MetaModelNamespace}.LongMapValueType`:
+ return 'Long';
+ case `${MetaModelNamespace}.DoubleMapValueType`:
+ return 'Double';
+ case `${MetaModelNamespace}.ObjectMapValueType`:
+ return String(this.ast.type.name);
+ default:
+ throw new Error(`Unrecognized map value type: ${this.getMetaType()}`);
+ }
+ }
+
+ /**
+ * Returns true if this model element is the definition of a participant.
+ *
+ * @return {boolean} true if the model element is an asset
+ */
+ isParticipant() {
+ return this.type === `${MetaModelNamespace}.ParticipantDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of a transaction.
+ *
+ * @return {boolean} true if the model element is an asset
+ */
+ isTransaction() {
+ return this.type === `${MetaModelNamespace}.TransactionDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of an event.
+ *
+ * @return {boolean} true if the model element is an asset
+ */
+ isEvent() {
+ return this.type === `${MetaModelNamespace}.EventDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of a concept.
+ * @return {boolean} true if the model element is an concept
+ */
+ isConcept() {
+ return this.type === `${MetaModelNamespace}.ConceptDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of a map.
+ * @deprecated replaced by isMap
+ * @return {boolean} true if the model element is a map
+ */
+ isMapDeclaration() {
+ return this.isMap();
+ }
+
+ /**
+ * Returns true if this model element is the definition of a map.
+ *
+ * @return {boolean} true if the model element is a map
+ */
+ isMap() {
+ return this.type === `${MetaModelNamespace}.MapDeclaration`;
+ }
+
+ /**
+ * Returns true if this model element is the definition of a property.
+ *
+ * @return {boolean} true if the model element is a property
+ */
+ isProperty() {
+ return this.declarationKind().endsWith('Property');
+ }
+
+ /**
+ * Returns true if this model element is the definition of a relationship property.
+ *
+ * @return {boolean} true if the model element is a relationship property
+ */
+ isRelationship() {
+ return this.declarationKind() === 'RelationshipProperty';
+ }
+
+ /**
+ * Returns true if this model element is the definition of a field. A field
+ * is a property that is not a relationship.
+ *
+ * @return {boolean} true if the model element is a field
+ */
+ isField() {
+ return this.isProperty() && !this.isRelationship();
+ }
+
+ /**
+ * Returns true if this model element is the definition of an enum value.
+ *
+ * @return {boolean} true if the model element is an enum value
+ */
+ isEnumValue() {
+ return this.declarationKind() === 'EnumProperty';
+ }
+
+ /**
+ * Returns true if this model element is the definition of a decorator.
+ *
+ * @return {boolean} true if the class is a decorator
+ */
+ isDecorator() {
+ return this.declarationKind() === 'Decorator';
+ }
+
+ /**
+ * Returns the string representation of this model element
+ * @return {String} the string representation of the model element
+ */
+ toString() {
+ return `${this.declarationKind()} {id=${this.getFullyQualifiedName()}}`;
+ }
+}
+
+module.exports = ModelElement;
\ No newline at end of file
diff --git a/packages/concerto-core/src/introspect/modelfile.js b/packages/concerto-core/src/introspect/modelfile.js
index 1c164ebca5..1bff4ea165 100644
--- a/packages/concerto-core/src/introspect/modelfile.js
+++ b/packages/concerto-core/src/introspect/modelfile.js
@@ -17,6 +17,7 @@
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const semver = require('semver');
+const Decorated = require('./decorated');
const AssetDeclaration = require('./assetdeclaration');
const EnumDeclaration = require('./enumdeclaration');
const ClassDeclaration = require('./classdeclaration');
@@ -29,7 +30,6 @@ const IllegalModelException = require('./illegalmodelexception');
const MapDeclaration = require('./mapdeclaration');
const ModelUtil = require('../modelutil');
const Globalize = require('../globalize');
-const Decorated = require('./decorated');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -38,6 +38,7 @@ if (global === undefined) {
const ClassDeclaration = require('./classdeclaration');
const ModelManager = require('../modelmanager');
const Declaration = require('./declaration');
+ const Decorator = require('./decorator');
}
/* eslint-enable no-unused-vars */
@@ -48,7 +49,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class ModelFile extends Decorated {
+class ModelFile {
/**
* Create a ModelFile. This should only be called by framework code.
* Use the ModelManager to manage ModelFiles.
@@ -60,9 +61,9 @@ class ModelFile extends Decorated {
* @throws {IllegalModelException}
*/
constructor(modelManager, ast, definitions, fileName) {
- super(ast);
this.modelManager = modelManager;
this.external = false;
+ this.decorated = null;
this.declarations = [];
this.localTypes = null;
this.imports = [];
@@ -93,8 +94,6 @@ class ModelFile extends Decorated {
this.external = fileName.startsWith('@');
}
- // Set up the decorators.
- this.process();
// Populate from the AST
this.fromAst(this.ast);
// Check version compatibility
@@ -109,6 +108,24 @@ class ModelFile extends Decorated {
}
}
+ /**
+ * Returns the decorators for this model file.
+ *
+ * @return {Decorator[]} the decorators for the model file
+ */
+ getDecorators() {
+ return this.decorated ? this.decorated.getDecorators() : [];
+ }
+
+ /**
+ * Returns the decorator for this model file with a given name.
+ * @param {string} name - the name of the decorator
+ * @return {Decorator} the decorator attached to this model file with the given name, or null if it does not exist.
+ */
+ getDecorator(name) {
+ return this.decorated ? this.decorated.getDecorator(name) : null;
+ }
+
/**
* Returns the ModelFile that defines this class.
*
@@ -218,8 +235,6 @@ class ModelFile extends Decorated {
* @protected
*/
validate() {
- super.validate();
-
// A dictionary of imports to versions to track unique namespaces
const importsMap = new Map();
@@ -270,6 +285,11 @@ class ModelFile extends Decorated {
let classDeclaration = this.declarations[n];
classDeclaration.validate();
}
+
+ // Set up the decorators, we fake a name for the model element
+ this.decorated = new Decorated(this, this.ast);
+ this.decorated.process();
+ this.decorated.validate();
}
/**
@@ -361,7 +381,6 @@ class ModelFile extends Decorated {
* For primitive types the type name is returned.
* @param {string} type - a FQN or short type name
* @return {string | ClassDeclaration} the class declaration for the type or null.
- * @private
*/
getType(type) {
// is the type a primitive?
@@ -591,15 +610,22 @@ class ModelFile extends Decorated {
/**
* Get the instances of a given type in this ModelFile
- * @param {Function} type - the type of the declaration
+ * @param {Function|string} type - the type of the declaration
+ * (either a short metamodel name or a constructor function for a type)
* @return {Object[]} the ClassDeclaration defined in the model file
*/
getDeclarations(type) {
let result = [];
for(let n=0; n < this.declarations.length; n++) {
let declaration = this.declarations[n];
- if(declaration instanceof type) {
- result.push(declaration);
+ if(type instanceof Function) {
+ if(declaration instanceof type) {
+ result.push(declaration);
+ }
+ } else {
+ if(declaration.declarationKind() === type) {
+ result.push(declaration);
+ }
}
}
diff --git a/packages/concerto-core/src/introspect/participantdeclaration.js b/packages/concerto-core/src/introspect/participantdeclaration.js
index 1050fc5031..1ae121c31d 100644
--- a/packages/concerto-core/src/introspect/participantdeclaration.js
+++ b/packages/concerto-core/src/introspect/participantdeclaration.js
@@ -14,7 +14,7 @@
'use strict';
-const IdentifiedDeclaration = require('./identifieddeclaration');
+const ClassDeclaration = require('./classdeclaration');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -31,7 +31,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class ParticipantDeclaration extends IdentifiedDeclaration {
+class ParticipantDeclaration extends ClassDeclaration {
/**
* Create an ParticipantDeclaration.
* @param {ModelFile} modelFile the ModelFile for this class
@@ -41,15 +41,6 @@ class ParticipantDeclaration extends IdentifiedDeclaration {
constructor(modelFile, ast) {
super(modelFile, ast);
}
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'ParticipantDeclaration';
- }
}
module.exports = ParticipantDeclaration;
diff --git a/packages/concerto-core/src/introspect/property.js b/packages/concerto-core/src/introspect/property.js
index db041f0d0b..acd26182b5 100644
--- a/packages/concerto-core/src/introspect/property.js
+++ b/packages/concerto-core/src/introspect/property.js
@@ -17,7 +17,6 @@
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const ModelUtil = require('../modelutil');
-const IllegalModelException = require('./illegalmodelexception');
const Decorated = require('./decorated');
// Types needed for TypeScript generation.
@@ -25,14 +24,13 @@ const Decorated = require('./decorated');
/* istanbul ignore next */
if (global === undefined) {
const ClassDeclaration = require('./classdeclaration');
- const ModelFile = require('./modelfile');
}
/* eslint-enable no-unused-vars */
/**
* Property representing an attribute of a class declaration,
- * either a Field or a Relationship.
+ * either a Field or a Relationship. Properties may be array or be optional.
*
* @class
* @memberof module:concerto-core
@@ -45,21 +43,11 @@ class Property extends Decorated {
* @throws {IllegalModelException}
*/
constructor(parent, ast) {
- super(ast);
+ super(parent.getModelFile(), ast);
this.parent = parent;
this.process();
}
- /**
- * Returns the ModelFile that defines this class.
- *
- * @public
- * @return {ModelFile} the owning ModelFile
- */
- getModelFile() {
- return this.parent.getModelFile();
- }
-
/**
* Returns the owner of this property
* @return {ClassDeclaration} the parent class declaration
@@ -76,43 +64,34 @@ class Property extends Decorated {
process() {
super.process();
- if (!ModelUtil.isValidIdentifier(this.ast.name)){
- throw new IllegalModelException(`Invalid property name '${this.ast.name}'`, this.modelFile, this.ast.location);
- }
-
- this.name = this.ast.name;
- this.decorator = null;
+ this.propertyType = null;
- if(!this.name) {
- throw new Error('No name for type ' + JSON.stringify(this.ast));
- }
-
- switch (this.ast.$class) {
+ switch (this.getMetaType()) {
case `${MetaModelNamespace}.EnumProperty`:
break;
case `${MetaModelNamespace}.BooleanProperty`:
- this.type = 'Boolean';
+ this.propertyType = 'Boolean';
break;
case `${MetaModelNamespace}.DateTimeProperty`:
- this.type = 'DateTime';
+ this.propertyType = 'DateTime';
break;
case `${MetaModelNamespace}.DoubleProperty`:
- this.type = 'Double';
+ this.propertyType = 'Double';
break;
case `${MetaModelNamespace}.IntegerProperty`:
- this.type = 'Integer';
+ this.propertyType = 'Integer';
break;
case `${MetaModelNamespace}.LongProperty`:
- this.type = 'Long';
+ this.propertyType = 'Long';
break;
case `${MetaModelNamespace}.StringProperty`:
- this.type = 'String';
+ this.propertyType = 'String';
break;
case `${MetaModelNamespace}.ObjectProperty`:
- this.type = this.ast.type ? this.ast.type.name : null;
+ this.propertyType = this.ast.type ? this.ast.type.name : null;
break;
case `${MetaModelNamespace}.RelationshipProperty`:
- this.type = this.ast.type.name;
+ this.propertyType = this.ast.type.name;
break;
}
this.array = false;
@@ -127,6 +106,10 @@ class Property extends Decorated {
else {
this.optional = false;
}
+
+ if(!this.ast.name) {
+ throw new Error(`Property of type ${this.propertyType} must have a name.`);
+ }
}
/**
@@ -138,30 +121,35 @@ class Property extends Decorated {
validate(classDecl) {
super.validate();
- if(this.type) {
- classDecl.getModelFile().resolveType( 'property ' + this.getFullyQualifiedName(), this.type);
+ if(this.propertyType) {
+ classDecl.getModelFile().resolveType( 'property ' + this.getFullyQualifiedName(), this.propertyType);
}
}
/**
- * Returns the name of a property
- * @return {string} the name of this field
+ * Returns the type of a property.
+ * @deprecated replaced by getPropertyType()
+ * @return {string} the type of this property
*/
- getName() {
- return this.name;
+ getType() {
+ return this.getPropertyType();
}
/**
- * Returns the type of a property
- * @return {string} the type of this field
+ * Returns the type of a property. This will return either: a primitive type
+ * name (String, Boolean, Integer etc) or the name of a non-primitive type,
+ * or will return null if this is an enum property.
+ *
+ * Note this is NOT the same as getMetaType() which returns the meta type for the property.
+ * @return {string|null} the type of this property or null if this is an enum property
*/
- getType() {
- return this.type;
+ getPropertyType() {
+ return this.propertyType;
}
/**
* Returns true if the field is optional
- * @return {boolean} true if the field is optional
+ * @return {boolean} true if the property is optional
*/
isOptional() {
return this.optional;
@@ -173,7 +161,7 @@ class Property extends Decorated {
*/
getFullyQualifiedTypeName() {
if(this.isPrimitive()) {
- return this.type;
+ return this.propertyType;
}
const parent = this.getParent();
@@ -184,9 +172,9 @@ class Property extends Decorated {
if(!modelFile) {
throw new Error('Parent of property ' + this.name + ' does not have a ModelFile!');
}
- const result = modelFile.getFullyQualifiedTypeName(this.type);
+ const result = modelFile.getFullyQualifiedTypeName(this.propertyType);
if(!result) {
- throw new Error('Failed to find fully qualified type name for property ' + this.name + ' with type ' + this.type );
+ throw new Error('Failed to find fully qualified type name for property ' + this.name + ' with type ' + this.propertyType );
}
return result;
@@ -197,15 +185,7 @@ class Property extends Decorated {
* @return {string} the fully qualified name of this property
*/
getFullyQualifiedName() {
- return this.getParent().getFullyQualifiedName() + '.' + this.getName();
- }
-
- /**
- * Returns the namespace of the parent of this property
- * @return {string} the namespace of the parent of this property
- */
- getNamespace() {
- return this.getParent().getNamespace();
+ return `${this.getParent().getFullyQualifiedName()}.${this.getName()}`;
}
/**
@@ -216,17 +196,25 @@ class Property extends Decorated {
return this.array;
}
-
/**
* Returns true if the field is declared as an enumerated value
+ * @deprecated replaced by isPropertyEnum()
* @return {boolean} true if the property is an enumerated value
*/
isTypeEnum() {
+ return this.isPropertyEnum();
+ }
+
+ /**
+ * Returns true if the field is declared as an enumerated value
+ * @return {boolean} true if the property is an enumerated value
+ */
+ isPropertyEnum() {
if(this.isPrimitive()) {
return false;
}
else {
- const type = this.getParent().getModelFile().getType(this.getType());
+ const type = this.getModelFile().getType(this.getPropertyType());
return type.isEnum();
}
}
@@ -236,7 +224,7 @@ class Property extends Decorated {
* @return {boolean} true if the property is a primitive type.
*/
isPrimitive() {
- return ModelUtil.isPrimitiveType(this.getType());
+ return ModelUtil.isPrimitiveType(this.getPropertyType());
}
}
diff --git a/packages/concerto-core/src/introspect/relationshipdeclaration.js b/packages/concerto-core/src/introspect/relationshipproperty.js
similarity index 86%
rename from packages/concerto-core/src/introspect/relationshipdeclaration.js
rename to packages/concerto-core/src/introspect/relationshipproperty.js
index 02dbeb8393..06c5d290ec 100644
--- a/packages/concerto-core/src/introspect/relationshipdeclaration.js
+++ b/packages/concerto-core/src/introspect/relationshipproperty.js
@@ -34,7 +34,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class RelationshipDeclaration extends Property {
+class RelationshipProperty extends Property {
/**
* Create a Relationship.
* @param {ClassDeclaration} parent - The owner of this property
@@ -54,14 +54,14 @@ class RelationshipDeclaration extends Property {
validate(classDecl) {
super.validate(classDecl);
// relationship cannot point to primitive types
- if(!this.getType()) {
+ if(!this.getPropertyType()) {
throw new IllegalModelException('Relationship must have a type', classDecl.getModelFile(), this.ast.location);
}
let classDeclaration = null;
// you can't have a relationship with a primitive...
- if(ModelUtil.isPrimitiveType(this.getType())) {
+ if(ModelUtil.isPrimitiveType(this.getPropertyType())) {
throw new IllegalModelException('Relationship ' + this.getName() + ' cannot be to the primitive type ' + this.getType(), classDecl.getModelFile(), this.ast.location );
} else {
let namespace = this.getParent().getNamespace();
@@ -69,7 +69,7 @@ class RelationshipDeclaration extends Property {
// we first try to get the type from our own model file
// because during validate we have not yet been added to the model manager
if(namespace === ModelUtil.getNamespace(this.getFullyQualifiedTypeName())) {
- classDeclaration = this.getParent().getModelFile().getType(this.getType());
+ classDeclaration = this.getParent().getModelFile().getType(this.getPropertyType());
}
else {
// otherwise we have to use the modelmanager to try to load
@@ -97,17 +97,8 @@ class RelationshipDeclaration extends Property {
* @return {String} the string version of the property.
*/
toString() {
- return 'RelationshipDeclaration {name=' + this.name + ', type=' + this.getFullyQualifiedTypeName() + ', array=' + this.array + ', optional=' + this.optional +'}';
- }
-
- /**
- * Returns true if this class is the definition of a relationship.
- *
- * @return {boolean} true if the class is a relationship
- */
- isRelationship() {
- return true;
+ return 'RelationshipProperty {name=' + this.name + ', type=' + this.getFullyQualifiedTypeName() + ', array=' + this.array + ', optional=' + this.optional +'}';
}
}
-module.exports = RelationshipDeclaration;
+module.exports = RelationshipProperty;
diff --git a/packages/concerto-core/src/introspect/scalardeclaration.js b/packages/concerto-core/src/introspect/scalardeclaration.js
index 3fab478621..1219a8bbca 100644
--- a/packages/concerto-core/src/introspect/scalardeclaration.js
+++ b/packages/concerto-core/src/introspect/scalardeclaration.js
@@ -17,7 +17,6 @@
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const Declaration = require('./declaration');
-const IllegalModelException = require('./illegalmodelexception');
const NumberValidator = require('./numbervalidator');
const StringValidator = require('./stringvalidator');
@@ -26,7 +25,6 @@ const StringValidator = require('./stringvalidator');
/* istanbul ignore next */
if (global === undefined) {
const Validator = require('./validator');
- const ClassDeclaration = require('./classdeclaration');
}
/* eslint-enable no-unused-vars */
@@ -51,30 +49,24 @@ class ScalarDeclaration extends Declaration {
process() {
super.process();
- this.superType = null;
- this.superTypeDeclaration = null;
- this.idField = null;
- this.timestamped = false;
- this.abstract = false;
this.validator = null;
+ this.scalarType = null;
if (this.ast.$class === `${MetaModelNamespace}.BooleanScalar`) {
- this.type = 'Boolean';
+ this.scalarType = 'Boolean';
} else if (this.ast.$class === `${MetaModelNamespace}.IntegerScalar`) {
- this.type = 'Integer';
+ this.scalarType = 'Integer';
} else if (this.ast.$class === `${MetaModelNamespace}.LongScalar`) {
- this.type = 'Long';
+ this.scalarType = 'Long';
} else if (this.ast.$class === `${MetaModelNamespace}.DoubleScalar`) {
- this.type = 'Double';
+ this.scalarType = 'Double';
} else if (this.ast.$class === `${MetaModelNamespace}.StringScalar`) {
- this.type = 'String';
+ this.scalarType = 'String';
} else if (this.ast.$class === `${MetaModelNamespace}.DateTimeScalar`) {
- this.type = 'DateTime';
- } else {
- this.type = null;
+ this.scalarType = 'DateTime';
}
- switch(this.getType()) {
+ switch(this.scalarType) {
case 'Integer':
case 'Double':
case 'Long':
@@ -97,87 +89,12 @@ class ScalarDeclaration extends Declaration {
}
/**
- * Semantic validation of the structure of this class. Subclasses should
- * override this method to impose additional semantic constraints on the
- * contents/relations of fields.
- *
- * @throws {IllegalModelException}
- * @protected
- */
- validate() {
- super.validate();
-
- const declarations = this.getModelFile().getAllDeclarations();
- const declarationNames = declarations.map(
- d => d.getFullyQualifiedName()
- );
- const uniqueNames = new Set(declarationNames);
-
- if (uniqueNames.size !== declarations.length) {
- const duplicateElements = declarationNames.filter(
- (item, index) => declarationNames.indexOf(item) !== index
- );
- throw new IllegalModelException(
- `Duplicate class name ${duplicateElements[0]}`
- );
- }
- }
-
- /**
- * Returns false as scalars are never identified.
- * @returns {Boolean} false as scalars are never identified
- * @deprecated
- */
- isIdentified() {
- return false;
- }
-
- /**
- * Returns false as scalars are never identified.
- * @returns {Boolean} false as scalars are never identified
- * @deprecated
- */
- isSystemIdentified() {
- return false;
- }
-
- /**
- * Returns null as scalars are never identified.
- * @return {string} as scalars are never identified
- * @deprecated
- */
- getIdentifierFieldName() {
- return null;
- }
-
- /**
- * Returns the FQN of the super type for this class or null if this
- * class does not have a super type.
+ * Returns the underlying primitive type of this scalar
*
- * @return {string} the FQN name of the super type or null
+ * @return {string} the type of the scalar (String, Integer, Long etc.)
*/
- getType() {
- return this.type;
- }
-
- /**
- * Returns the FQN of the super type for this class or null if this
- * class does not have a super type.
- *
- * @return {string} the FQN name of the super type or null
- * @deprecated
- */
- getSuperType() {
- return null;
- }
-
- /**
- * Get the super type class declaration for this class.
- * @return {ClassDeclaration} the super type declaration, or null if there is no super type.
- * @deprecated
- */
- getSuperTypeDeclaration() {
- return null;
+ getScalarType() {
+ return this.scalarType;
}
/**
@@ -206,78 +123,9 @@ class ScalarDeclaration extends Declaration {
* @return {String} the string representation of the class
*/
toString() {
- return 'ScalarDeclaration {id=' + this.getFullyQualifiedName() + '}';
+ return 'ScalarDeclaration {id=' + this.getFullyQualifiedName() +
+ ' scalarType=' + this.scalarType + '}';
}
-
- /**
- * Returns true if this class is abstract.
- *
- * @return {boolean} true if the class is abstract
- * @deprecated
- */
- isAbstract() {
- return true;
- }
-
- /**
- * Returns true if this class is the definition of a scalar declaration.
- *
- * @return {boolean} true if the class is a scalar
- */
- isScalarDeclaration() {
- return true;
- }
-
- /**
- * Returns true if this class is the definition of an asset.
- *
- * @return {boolean} true if the class is an asset
- * @deprecated
- */
- isAsset() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a participant.
- *
- * @return {boolean} true if the class is a participant
- * @deprecated
- */
- isParticipant() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a transaction.
- *
- * @return {boolean} true if the class is a transaction
- * @deprecated
- */
- isTransaction() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of an event.
- *
- * @return {boolean} true if the class is an event
- * @deprecated
- */
- isEvent() {
- return false;
- }
-
- /**
- * Returns true if this class is the definition of a concept.
- *
- * @return {boolean} true if the class is a concept
- * @deprecated
- */
- isConcept() {
- return false;
- }
-
}
module.exports = ScalarDeclaration;
diff --git a/packages/concerto-core/src/introspect/transactiondeclaration.js b/packages/concerto-core/src/introspect/transactiondeclaration.js
index b7b43e46fb..afd996aaeb 100644
--- a/packages/concerto-core/src/introspect/transactiondeclaration.js
+++ b/packages/concerto-core/src/introspect/transactiondeclaration.js
@@ -14,7 +14,7 @@
'use strict';
-const IdentifiedDeclaration = require('./identifieddeclaration');
+const ClassDeclaration = require('./classdeclaration');
// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
@@ -31,7 +31,7 @@ if (global === undefined) {
* @class
* @memberof module:concerto-core
*/
-class TransactionDeclaration extends IdentifiedDeclaration {
+class TransactionDeclaration extends ClassDeclaration {
/**
* Create an TransactionDeclaration.
* @param {ModelFile} modelFile the ModelFile for this class
@@ -41,15 +41,6 @@ class TransactionDeclaration extends IdentifiedDeclaration {
constructor(modelFile, ast) {
super(modelFile, ast);
}
-
- /**
- * Returns the kind of declaration
- *
- * @return {string} what kind of declaration this is
- */
- declarationKind() {
- return 'TransactionDeclaration';
- }
}
module.exports = TransactionDeclaration;
diff --git a/packages/concerto-core/src/messages/en.json b/packages/concerto-core/src/messages/en.json
index 910b20822e..f1bb58d299 100644
--- a/packages/concerto-core/src/messages/en.json
+++ b/packages/concerto-core/src/messages/en.json
@@ -65,6 +65,8 @@
"resourcevalidator-fieldtypeviolation": "Model violation in the \"{resourceId}\" instance. The field \"{propertyName}\" has a value of \"{value}\" (type of value: \"{typeOfValue}\"). Expected type of value: \"{fieldType}\".",
"resourcevalidator-missingrequiredproperty": "The instance \"{resourceId}\" is missing the required field \"{fieldName}\".",
"resourcevalidator-invalidenumvalue": "Model violation in the \"{resourceId}\" instance. Invalid enum value of \"{value}\" for the field \"{fieldName}\".",
+ "resourcevalidator-invalidmapkey": "Model violation in the \"{resourceId}\" instance. Invalid map key of \"{value}\".",
+ "resourcevalidator-invalidmapvalue": "Model violation in the \"{resourceId}\" instance. Invalid map value of \"{value}\".",
"resourcevalidator-abstractclass": "The class \"{className}\" is abstract and should not contain an instance.",
"resourcevalidator-undeclaredfield": "Instance \"{resourceId}\" has a property named \"{propertyName}\", which is not declared in \"{fullyQualifiedTypeName}\".",
"resourcevalidator-invalidfieldassignment": "Instance \"{resourceId}\" has a property \"{propertyName}\" with type \"{objectType}\" that is not derived from \"{fieldType}\".",
diff --git a/packages/concerto-core/src/modelutil.js b/packages/concerto-core/src/modelutil.js
index 887520700a..069225116c 100644
--- a/packages/concerto-core/src/modelutil.js
+++ b/packages/concerto-core/src/modelutil.js
@@ -338,6 +338,34 @@ class ModelUtil {
`${MetaModelNamespace}.ObjectMapValueType`
].includes(value.$class);
}
+
+ /**
+ * Visits the model element.
+ * @param {ModelElement} modelElement the model element
+ * @param {*} parameters the visitor params
+ * @param {*} visitor the visitor
+ * @returns {*} result of visit
+ */
+ static dispatch(modelElement, parameters, visitor) {
+ if(!modelElement.isMap) {
+ throw new Error('Model element is invalid.');
+ }
+ if (modelElement.isMap()) {
+ return visitor.visitMapDeclaration(modelElement, parameters);
+ } else if (modelElement.isEnum()) {
+ return visitor.visitEnumDeclaration(modelElement, parameters);
+ } else if (modelElement.isDeclaration()) {
+ return visitor.visitClassDeclaration(modelElement, parameters);
+ } else if (modelElement.isRelationship()) {
+ return visitor.visitRelationshipDeclaration(modelElement, parameters);
+ } else if (modelElement.isField()) {
+ const field = modelElement.isTypeScalar?.() ? modelElement.getScalarField() : modelElement;
+ return visitor.visitField(field, parameters);
+ } else {
+ throw new Error(`Unrecognised model element: ${modelElement.toString()}` );
+ }
+ }
}
+
module.exports = ModelUtil;
diff --git a/packages/concerto-core/src/serializer.js b/packages/concerto-core/src/serializer.js
index 817ba5d6ec..1f84ae7212 100644
--- a/packages/concerto-core/src/serializer.js
+++ b/packages/concerto-core/src/serializer.js
@@ -173,7 +173,7 @@ class Serializer {
resource = this.factory.newConcept(classDeclaration.getNamespace(),
classDeclaration.getName(),
jsonObject[classDeclaration.getIdentifierFieldName()] );
- } else if (classDeclaration.isMapDeclaration?.()) {
+ } else if (classDeclaration.isMap()) {
throw new Error('Attempting to create a Map declaration is not supported.');
} else if (classDeclaration.isEnum()) {
throw new Error('Attempting to create an ENUM declaration is not supported.');
@@ -190,7 +190,7 @@ class Serializer {
parameters.resourceStack = new TypedStack(resource);
parameters.modelManager = this.modelManager;
parameters.factory = this.factory;
- const populator = new JSONPopulator(options.acceptResourcesForRelationships === true, false, options.utcOffset, options.strictQualifiedDateTimes === true);
+ const populator = new JSONPopulator(options.acceptResourcesForRelationships === true, options.utcOffset, options.strictQualifiedDateTimes === true);
classDeclaration.accept(populator, parameters);
// validate the resource against the model
diff --git a/packages/concerto-core/src/serializer/instancegenerator.js b/packages/concerto-core/src/serializer/instancegenerator.js
index 1055eaf8ac..1277bb9cfe 100644
--- a/packages/concerto-core/src/serializer/instancegenerator.js
+++ b/packages/concerto-core/src/serializer/instancegenerator.js
@@ -15,6 +15,7 @@
'use strict';
const Util = require('../util');
+const ModelUtil = require('../modelutil');
const Globalize = require('../globalize');
/**
@@ -35,19 +36,7 @@ class InstanceGenerator {
* @private
*/
visit(thing, parameters) {
- if (thing.isClassDeclaration?.()) {
- return this.visitClassDeclaration(thing, parameters);
- } else if (thing.isMapDeclaration?.()) {
- return this.visitMapDeclaration(thing, parameters);
- } else if (thing.isRelationship?.()) {
- return this.visitRelationshipDeclaration(thing, parameters);
- } else if (thing.isTypeScalar?.()) {
- return this.visitField(thing.getScalarField(), parameters);
- } else if (thing.isField?.()) {
- return this.visitField(thing, parameters);
- } else {
- throw new Error('Unrecognised ' + JSON.stringify(thing) );
- }
+ return ModelUtil.dispatch(thing, parameters, this);
}
/**
@@ -213,7 +202,7 @@ class InstanceGenerator {
/**
* Visitor design pattern
- * @param {RelationshipDeclaration} relationshipDeclaration - the object being visited
+ * @param {RelationshipProperty} relationshipDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Relationship} the result of visiting
* @private
diff --git a/packages/concerto-core/src/serializer/jsongenerator.js b/packages/concerto-core/src/serializer/jsongenerator.js
index f211a897d8..e8b970d149 100644
--- a/packages/concerto-core/src/serializer/jsongenerator.js
+++ b/packages/concerto-core/src/serializer/jsongenerator.js
@@ -61,19 +61,7 @@ class JSONGenerator {
* @private
*/
visit(thing, parameters) {
- if (thing.isClassDeclaration?.()) {
- return this.visitClassDeclaration(thing, parameters);
- } else if (thing.isRelationship?.()) {
- return this.visitRelationshipDeclaration(thing, parameters);
- }else if (thing.isMapDeclaration?.()) {
- return this.visitMapDeclaration(thing, parameters);
- } else if (thing.isTypeScalar?.()) {
- return this.visitField(thing.getScalarField(), parameters);
- } else if (thing.isField?.()) {
- return this.visitField(thing, parameters);
- } else {
- throw new Error('Unrecognised ' + JSON.stringify(thing));
- }
+ return ModelUtil.dispatch(thing, parameters, this);
}
/**
@@ -86,30 +74,24 @@ class JSONGenerator {
visitMapDeclaration(mapDeclaration, parameters) {
const obj = parameters.stack.pop();
- // initialise Map with $class property
- let map = new Map();
+ const map = new Map();
+ const mapKeyField = mapDeclaration.getKey().toField();
+ const mapValueField = mapDeclaration.getValue().toField();
obj.forEach((value, key) => {
-
// don't serialize System Properties, other than $class
if(ModelUtil.isSystemProperty(key)) {
return;
}
- // Key is always a string, but value might be a ValidatedResource.
- if (typeof value === 'object') {
- let decl = mapDeclaration.getModelFile()
- .getAllDeclarations()
- .find(decl => decl.name === value.getType());
-
- // convert declaration to JSON representation
- parameters.stack.push(value);
- const jsonValue = decl.accept(this, parameters);
-
- value = jsonValue;
- }
+ // convert the key
+ parameters.stack.push(key);
+ const keyJson = mapKeyField.accept(this, parameters);
- map.set(key, value);
+ // convert the value
+ parameters.stack.push(value);
+ const valueJson = mapValueField.accept(this, parameters);
+ map.set(keyJson, valueJson);
});
return Object.fromEntries(map);
@@ -195,6 +177,7 @@ class JSONGenerator {
result = mapDeclaration.accept(this, parameters);
}
else {
+ // concepts
parameters.stack.push(obj);
const classDeclaration = parameters.modelManager.getType(obj.getFullyQualifiedType());
result = classDeclaration.accept(this, parameters);
@@ -233,7 +216,7 @@ class JSONGenerator {
/**
* Visitor design pattern
- * @param {RelationshipDeclaration} relationshipDeclaration - the object being visited
+ * @param {RelationshipProperty} relationshipDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
@@ -286,7 +269,7 @@ class JSONGenerator {
/**
* Returns the persistent format for a relationship.
- * @param {RelationshipDeclaration} relationshipDeclaration - the relationship being persisted
+ * @param {RelationshipProperty} relationshipDeclaration - the relationship being persisted
* @param {Identifiable} relationshipOrResource - the relationship or the resource
* @returns {string} the text to use to persist the relationship
*/
diff --git a/packages/concerto-core/src/serializer/jsonpopulator.js b/packages/concerto-core/src/serializer/jsonpopulator.js
index 891bf9c375..10a9d05c76 100644
--- a/packages/concerto-core/src/serializer/jsonpopulator.js
+++ b/packages/concerto-core/src/serializer/jsonpopulator.js
@@ -29,6 +29,14 @@ dayjs.extend(minMax);
const duration = require('dayjs/plugin/duration');
dayjs.extend(duration);
+// Types needed for TypeScript generation.
+/* eslint-disable no-unused-vars */
+/* istanbul ignore next */
+if (global === undefined) {
+ const ModelElement = require('../introspect/modelelement');
+}
+/* eslint-enable no-unused-vars */
+
/**
* Get all properties on a resource object that both have a value and are not system properties.
* @param {Object} resourceData JSON object representation of a resource.
@@ -94,12 +102,11 @@ class JSONPopulator {
* Constructor.
* @param {boolean} [acceptResourcesForRelationships] Permit resources in the
* place of relationships, false by default.
- * @param {boolean} [ergo] - Deprecated - This is a dummy parameter to avoid breaking any consumers. It will be removed in a future release.
* @param {number} [utcOffset] - UTC Offset for DateTime values.
* @param {number} [strictQualifiedDateTimes] - Only allow fully-qualified date-times with offsets.
*/
- constructor(acceptResourcesForRelationships, ergo, utcOffset, strictQualifiedDateTimes) {
+ constructor(acceptResourcesForRelationships, utcOffset, strictQualifiedDateTimes) {
this.acceptResourcesForRelationships = acceptResourcesForRelationships;
this.utcOffset = utcOffset || 0; // Defaults to UTC
this.strictQualifiedDateTimes = strictQualifiedDateTimes;
@@ -111,27 +118,14 @@ class JSONPopulator {
/**
* Visitor design pattern
- * @param {Object} thing - the object being visited
+ * @param {ModelElement} modelElement - the model element being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
- visit(thing, parameters = {}) {
+ visit(modelElement, parameters = {}) {
parameters.path ?? (parameters.path = new TypedStack('$'));
-
- if (thing.isClassDeclaration?.()) {
- return this.visitClassDeclaration(thing, parameters);
- } else if (thing.isMapDeclaration?.()) {
- return this.visitMapDeclaration(thing, parameters);
- } else if (thing.isRelationship?.()) {
- return this.visitRelationshipDeclaration(thing, parameters);
- } else if (thing.isTypeScalar?.()) {
- return this.visitField(thing.getScalarField(), parameters);
- } else if (thing.isField?.()) {
- return this.visitField(thing, parameters);
- } else {
- throw new Error('Unrecognised ' + JSON.stringify(thing) );
- }
+ return ModelUtil.dispatch(modelElement, parameters, this);
}
/**
@@ -177,57 +171,26 @@ class JSONPopulator {
getAssignableProperties(jsonObj, mapDeclaration);
jsonObj = new Map(Object.entries(jsonObj));
-
- let map = new Map();
+ const map = new Map();
+ const mapKeyField = mapDeclaration.getKey().toField();
+ const mapValueField = mapDeclaration.getValue().toField();
jsonObj.forEach((value, key) => {
+ // convert the key
+ parameters.jsonStack.push(key);
+ parameters.path.push(key);
+ const keyJson = mapKeyField.accept(this, parameters);
- if (key === '$class') {
- map.set(key, value);
- return;
- }
-
- if (!ModelUtil.isPrimitiveType(mapDeclaration.getKey().getType())) {
- key = this.processMapType(mapDeclaration, parameters, key, mapDeclaration.getKey().getType());
- }
-
- if (!ModelUtil.isPrimitiveType(mapDeclaration.getValue().getType())) {
- value = this.processMapType(mapDeclaration, parameters, value, mapDeclaration.getValue().getType());
- }
-
- map.set(key, value);
+ // convert the value
+ parameters.jsonStack.push(value);
+ parameters.path.push(key);
+ const valueJson = mapValueField.accept(this, parameters);
+ map.set(keyJson, valueJson);
});
return map;
}
- /**
- * Visitor design pattern
- * @param {MapDeclaration} mapDeclaration - the object being visited
- * @param {Object} parameters - the parameter
- * @param {Object} value - the key or value belonging to the Map Entry.
- * @param {Object} type - the Type associated with the Key or Value Map Entry.
- * @return {Object} value - the key or value belonging to the Map Entry.
- * @private
- */
- processMapType(mapDeclaration, parameters, value, type) {
- let decl = mapDeclaration.getModelFile()
- .getAllDeclarations()
- .find(decl => decl.name === type);
-
- // if its a ClassDeclaration, populate the Concept.
- if (decl?.isClassDeclaration()) {
- let subResource = parameters.factory.newConcept(decl.getNamespace(),
- decl.getName(), decl.getIdentifierFieldName() );
-
- parameters.jsonStack.push(value);
- parameters.resourceStack.push(subResource);
- return decl.accept(this, parameters);
- }
- // otherwise its a scalar value, we only need to return the primitve value of the scalar.
- return value;
- }
-
/**
* Visitor design pattern
* @param {Field} field - the object being visited
@@ -383,7 +346,7 @@ class JSONPopulator {
/**
* Visitor design pattern
- * @param {RelationshipDeclaration} relationshipDeclaration - the object being visited
+ * @param {RelationshipProperty} relationshipDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
diff --git a/packages/concerto-core/src/serializer/resourcevalidator.js b/packages/concerto-core/src/serializer/resourcevalidator.js
index 711fd6cefd..8a813fc810 100644
--- a/packages/concerto-core/src/serializer/resourcevalidator.js
+++ b/packages/concerto-core/src/serializer/resourcevalidator.js
@@ -67,19 +67,7 @@ class ResourceValidator {
* @private
*/
visit(thing, parameters) {
- if (thing.isEnum?.()) {
- return this.visitEnumDeclaration(thing, parameters);
- } else if (thing.isClassDeclaration?.()) {
- return this.visitClassDeclaration(thing, parameters);
- } else if (thing.isMapDeclaration?.()) {
- return this.visitMapDeclaration(thing, parameters);
- }else if (thing.isRelationship?.()) {
- return this.visitRelationshipDeclaration(thing, parameters);
- } else if (thing.isTypeScalar?.()) {
- return this.visitField(thing.getScalarField(), parameters);
- } else if (thing.isField?.()) {
- return this.visitField(thing, parameters);
- }
+ return ModelUtil.dispatch(thing, parameters, this);
}
/**
@@ -110,61 +98,6 @@ class ResourceValidator {
return null;
}
-
- /**
- * Check a Type that is declared as a Map Type.
- * @param {Object} type - the type in scope for validation, can be MapTypeKey or MapTypeValue
- * @param {Object} value - the object being validated
- * @param {Object} parameters - the parameter
- * @param {Map} mapDeclaration - the object being visited
- * @private
- */
- checkMapType(type, value, parameters, mapDeclaration, ) {
-
- if (!ModelUtil.isPrimitiveType(type.getType())) {
-
- // thing might be a Concept, Scalar String, Scalar DateTime
- let thing = mapDeclaration.getModelFile()
- .getAllDeclarations()
- .find(decl => decl.name === type.getType());
-
- // if Key or Value is Scalar, get the Base Type of the Scalar for primitive validation.
- if (ModelUtil.isScalar(mapDeclaration.getKey())) {
- type = thing.getType();
- }
-
- if (thing?.isClassDeclaration?.()) {
- parameters.stack.push(value);
- thing.accept(this, parameters);
- return;
- }
- } else {
- // otherwise its a primitive
- type = type.getType();
-
- }
-
- // validate the primitive
- switch(type) {
- case 'String':
- if (typeof value !== 'string') {
- throw new Error(`Model violation in ${mapDeclaration.getFullyQualifiedName()}. Expected Type of String but found '${value}' instead.`);
- }
- break;
- case 'DateTime':
- if (!dayjs.utc(value).isValid()) {
- throw new Error(`Model violation in ${mapDeclaration.getFullyQualifiedName()}. Expected Type of DateTime but found '${value}' instead.`);
- }
- break;
- case 'Boolean':
- if (typeof value !== 'boolean') {
- const type = typeof value;
- throw new Error(`Model violation in ${mapDeclaration.getFullyQualifiedName()}. Expected Type of Boolean but found ${type} instead, for value '${value}'.`);
- }
- break;
- }
- }
-
/**
* Visitor design pattern
*
@@ -182,12 +115,17 @@ class ResourceValidator {
throw new Error('Expected a Map, but found ' + JSON.stringify(obj));
}
+ const mapKeyField = mapDeclaration.getKey().toField();
+ const mapValueField = mapDeclaration.getValue().toField();
+
obj.forEach((value, key) => {
if (!ModelUtil.isSystemProperty(key)) {
- // Validate Key
- this.checkMapType(mapDeclaration.getKey(), key, parameters, mapDeclaration);
- // Validate Value
- this.checkMapType(mapDeclaration.getValue(), value, parameters, mapDeclaration);
+ // validate key
+ parameters.stack.push(key);
+ mapKeyField.accept(this,parameters);
+ // validate value
+ parameters.stack.push(value);
+ mapValueField.accept(this,parameters);
}
});
@@ -302,7 +240,7 @@ class ResourceValidator {
ResourceValidator.reportFieldTypeViolation(parameters.rootResourceIdentifier, propName, obj, field);
}
- if(field.isTypeEnum()) {
+ if(field.isPropertyEnum()) {
this.checkEnum(obj, field,parameters);
}
else {
@@ -444,7 +382,7 @@ class ResourceValidator {
/**
* Visitor design pattern
- * @param {RelationshipDeclaration} relationshipDeclaration - the object being visited
+ * @param {RelationshipProperty} relationshipDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
@@ -556,7 +494,7 @@ class ResourceValidator {
/**
* Throw a new error for a model violation.
* @param {string} id - the identifier of this instance.
- * @param {RelationshipDeclaration} relationshipDeclaration - the declaration of the class
+ * @param {RelationshipProperty} relationshipDeclaration - the declaration of the class
* @param {Object} value - the value of the field.
* @private
*/
diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js
index ef8034b71b..92c69f0d31 100644
--- a/packages/concerto-core/test/decoratormanager.js
+++ b/packages/concerto-core/test/decoratormanager.js
@@ -63,7 +63,7 @@ describe('DecoratorManager', () => {
describe('#validate', function() {
it('should support syntax validation', async function() {
const dcs = fs.readFileSync('./test/data/decoratorcommands/web.json', 'utf-8');
- const validationModelManager = DecoratorManager.validate( JSON.parse(dcs));
+ const validationModelManager = DecoratorManager.validate(JSON.parse(dcs));
validationModelManager.should.not.be.null;
});
diff --git a/packages/concerto-core/test/introspect/classdeclaration.js b/packages/concerto-core/test/introspect/classdeclaration.js
index aee2ce838e..b1a867e466 100644
--- a/packages/concerto-core/test/introspect/classdeclaration.js
+++ b/packages/concerto-core/test/introspect/classdeclaration.js
@@ -47,7 +47,7 @@ describe('ClassDeclaration', () => {
describe('#constructor', () => {
- it('should throw if ast contains invalid type', () => {
+ it('should throw if ast does not contain $class', () => {
(() => {
new ClassDeclaration(modelFile, {
name: 'suchName',
@@ -55,16 +55,29 @@ describe('ClassDeclaration', () => {
$class: 'noSuchType'
}]
});
+ }).should.throw(/Invalid ModelElement; must have a \$class/);
+ });
+
+ it('should throw if ast contains invalid property type', () => {
+ (() => {
+ new ClassDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.ConceptDeclaration`,
+ name: 'suchName',
+ properties: [{
+ $class: 'noSuchType'
+ }]
+ });
}).should.throw(/Unrecognised model element/);
});
it('should throw for a bad identifier', () => {
(() => {
new ClassDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.ConceptDeclaration`,
name: '2nd',
properties: []
});
- }).should.throw(/Invalid class name '2nd'/);
+ }).should.throw(/Invalid model element name '2nd'/);
});
});
@@ -74,21 +87,21 @@ describe('ClassDeclaration', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/classdeclaration.dupeassetname.cto', AssetDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
it('should throw when transaction name is duplicted in a modelfile', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/classdeclaration.dupetransactionname.cto', TransactionDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
it('should throw when participant name is duplicted in a modelfile', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/classdeclaration.dupeparticipantname.cto', ParticipantDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
it('should throw when an super type identifier is redeclared', () => {
@@ -110,14 +123,14 @@ describe('ClassDeclaration', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/classdeclaration.dupeconceptname.cto', ConceptDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
it('should throw when enum name is duplicted in a modelfile', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/classdeclaration.dupeenumname.cto', EnumDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
it('should throw when not abstract, not enum and not concept without an identifier', () => {
@@ -147,6 +160,7 @@ describe('ClassDeclaration', () => {
it('should call the visitor', () => {
let clz = new ClassDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.ConceptDeclaration`,
name: 'suchName',
properties: [
]
@@ -165,12 +179,13 @@ describe('ClassDeclaration', () => {
it('should return the class name', () => {
let clz = new ClassDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.ConceptDeclaration`,
name: 'suchName',
properties: [
]
});
clz.getName().should.equal('suchName');
- clz.toString().should.equal('ClassDeclaration {id=com.hyperledger.testing@1.0.0.suchName super=Concept enum=false abstract=false}');
+ clz.toString().should.equal('ClassDeclaration {id=com.hyperledger.testing@1.0.0.suchName super=Concept declarationKind=ConceptDeclaration abstract=false idField=null}');
});
});
@@ -254,6 +269,7 @@ describe('ClassDeclaration', () => {
it('should return the fully qualified name if function is in a namespace', () => {
let clz = new ClassDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.ConceptDeclaration`,
name: 'suchName',
properties: [
]
@@ -297,7 +313,7 @@ describe('ClassDeclaration', () => {
it('toString',()=>{
const baseclass = modelManager.getType('com.testing.parent@1.0.0.Base');
- baseclass.toString().should.equal('ClassDeclaration {id=com.testing.parent@1.0.0.Base super=Participant enum=false abstract=true}');
+ baseclass.toString().should.equal('ClassDeclaration {id=com.testing.parent@1.0.0.Base super=Participant declarationKind=ParticipantDeclaration abstract=true idField=id}');
});
});
diff --git a/packages/concerto-core/test/introspect/declaration.js b/packages/concerto-core/test/introspect/declaration.js
index 2f184b029e..d1eedebcaf 100644
--- a/packages/concerto-core/test/introspect/declaration.js
+++ b/packages/concerto-core/test/introspect/declaration.js
@@ -18,62 +18,28 @@ const Declaration = require('../../src/introspect/declaration');
const ModelFile = require('../../src/introspect/modelfile');
require('chai').should();
-const should = require('chai').should();
const sinon = require('sinon');
describe('Declaration', () => {
let modelFile;
- let declaration;
beforeEach(() => {
modelFile = sinon.createStubInstance(ModelFile);
- declaration = new Declaration(modelFile, { ast: true });
});
describe('#constructor', () => {
- it('should throw if ast not specified', () => {
+ it('should throw if model file not specified', () => {
(() => {
new Declaration(null);
- }).should.throw(/ast not specified/);
- });
-
- });
-
- describe('#isIdentified', () => {
- it('should be false', () => {
- declaration.isIdentified().should.equal(false);
- });
- });
-
- describe('#isMapDeclaration', () => {
- it('should be false', () => {
- declaration.isMapDeclaration().should.equal(false);
- });
- });
-
- describe('#isSystemIdentified', () => {
- it('should be false', () => {
- declaration.isSystemIdentified().should.equal(false);
- });
- });
-
- describe('#getIdentifierFieldName', () => {
- it('should be null', () => {
- should.equal(declaration.getIdentifierFieldName(), null);
+ }).should.throw(/ModelFile not specified/);
});
- });
- describe('#getType', () => {
- it('should be null', () => {
- should.equal(declaration.getType(), null);
- });
- });
-
- describe('#toString', () => {
- it('should be null', () => {
- should.equal(declaration.toString(), null);
+ it('should throw if ast not specified', () => {
+ (() => {
+ new Declaration(modelFile, null);
+ }).should.throw(/ast not specified/);
});
});
});
diff --git a/packages/concerto-core/test/introspect/decorated.js b/packages/concerto-core/test/introspect/decorated.js
index 55e803379a..6a3a8a5ccd 100644
--- a/packages/concerto-core/test/introspect/decorated.js
+++ b/packages/concerto-core/test/introspect/decorated.js
@@ -23,31 +23,23 @@ const sinon = require('sinon');
describe('Decorated', () => {
let modelFile;
- let decorated;
beforeEach(() => {
modelFile = sinon.createStubInstance(ModelFile);
- decorated = new Decorated(modelFile, { ast: true });
});
describe('#constructor', () => {
- it('should throw if ast not specified', () => {
+ it('should throw if ModelFile not specified', () => {
(() => {
new Decorated(null);
- }).should.throw(/ast not specified/);
+ }).should.throw(/ModelFile not specified/);
});
- });
-
- describe('#getModelFile', () => {
-
- it('should throw as abstract', () => {
+ it('should throw if ast not specified', () => {
(() => {
- decorated.getModelFile();
- }).should.throw(/not implemented/);
+ new Decorated(modelFile, null);
+ }).should.throw(/ast not specified/);
});
-
});
-
});
diff --git a/packages/concerto-core/test/introspect/decorator.js b/packages/concerto-core/test/introspect/decorator.js
index 402a7985fe..ff60ed28ec 100644
--- a/packages/concerto-core/test/introspect/decorator.js
+++ b/packages/concerto-core/test/introspect/decorator.js
@@ -16,6 +16,7 @@
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
+const ModelFile = require('../../src/introspect/modelfile');
const AssetDeclaration = require('../../src/introspect/assetdeclaration');
const Decorator = require('../../src/introspect/decorator');
@@ -25,6 +26,7 @@ const sinon = require('sinon');
describe('Decorator', () => {
let mockAssetDeclaration;
+ let mockModelFile;
const ast = {
$class: `${MetaModelNamespace}.Decorator`,
@@ -47,13 +49,14 @@ describe('Decorator', () => {
beforeEach(() => {
mockAssetDeclaration = sinon.createStubInstance(AssetDeclaration);
+ mockModelFile = sinon.createStubInstance(ModelFile);
});
describe('#constructor', () => {
it('should store values', () => {
- const d = new Decorator(mockAssetDeclaration, ast);
+ const d = new Decorator(mockModelFile, mockAssetDeclaration, ast);
d.getParent().should.equal(mockAssetDeclaration);
d.getName().should.equal('Test');
d.getArguments().should.deep.equal(['one','two','three']);
@@ -65,7 +68,7 @@ describe('Decorator', () => {
describe('#accept', () => {
it('should call the visitor', () => {
- const d = new Decorator(mockAssetDeclaration, ast);
+ const d = new Decorator(mockModelFile, mockAssetDeclaration, ast);
let visitor = {
visit: sinon.stub()
};
diff --git a/packages/concerto-core/test/introspect/decorators.js b/packages/concerto-core/test/introspect/decorators.js
index 58f7735958..a1212990c0 100644
--- a/packages/concerto-core/test/introspect/decorators.js
+++ b/packages/concerto-core/test/introspect/decorators.js
@@ -190,23 +190,25 @@ describe('Decorators', () => {
* 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 {ModelFile} modelFile - the modelFile for the decorator
* @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) {
+ newDecorator(modelFile, parent, ast) {
if (ast.name !== 'bar') {
return null;
}
return new(class MyDecorator extends Decorator {
/**
* Create a Decorator.
+ * @param {ModelFile} modelFile - the modelFile for the 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);
+ constructor(modelFile, parent, ast) {
+ super(modelFile, parent, ast);
}
/**
* My method.
@@ -215,7 +217,7 @@ describe('Decorators', () => {
myMethod() {
return 'woop';
}
- })(parent, ast);
+ })(modelFile, parent, ast);
}
});
diff --git a/packages/concerto-core/test/introspect/identifieddeclaration.js b/packages/concerto-core/test/introspect/identifieddeclaration.js
deleted file mode 100644
index cdf1dd5915..0000000000
--- a/packages/concerto-core/test/introspect/identifieddeclaration.js
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * 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 ModelManager = require('../../src/modelmanager');
-const IdentifiedDeclaration = require('../../src/introspect/identifieddeclaration');
-
-require('chai').should();
-
-describe('IdentifiedDeclaration', () => {
-
- beforeEach(() => {
- });
-
- afterEach(() => {
- });
-
- describe('#hasInstance', () => {
-
- it('should identify instance', () => {
- const mm = new ModelManager();
- mm.addCTOModel( `
-namespace test@1.0.0
-
-asset Order {
- o Double price
-}
- `, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- (order instanceof IdentifiedDeclaration).should.be.true;
- });
- });
-
- describe('#identified', () => {
-
- it('should create a system identifier', () => {
- const mm = new ModelManager();
- mm.addCTOModel( `
-namespace test@1.0.0
-
-asset Order {
- o Double price
-}
- `, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- order.getProperties().length.should.equal(2);
- order.getIdentifierFieldName().should.equal('$identifier');
- });
-
- it('should allow declaring system identifier', () => {
- const mm = new ModelManager();
- mm.addCTOModel( `
-namespace test@1.0.0
-
-asset Order {
- o Double price
-}
- `, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- order.getProperties().length.should.equal(2);
- order.getIdentifierFieldName().should.equal('$identifier');
- });
-
- it('should allow declaring explicit identifier', () => {
- const mm = new ModelManager();
- mm.addCTOModel( `
-namespace test@1.0.0
-
-asset Order identified by sku {
- o String sku
- o Double price
-}
- `, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- order.getProperties().length.should.equal(3); // XXX Assets always have an identifier
- order.getIdentifierFieldName().should.equal('sku');
- order.isSystemIdentified().should.be.false;
- order.isExplicitlyIdentified().should.be.true;
- });
-
- it('should not allow overriding explicit identifier', () => {
- const mm = new ModelManager();
-
- (() => {
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset Order identified by sku {
- o String sku
- o Double price
- }
-
- asset FancyOrder identified extends Order {
- }
-
- `, 'test.cto');
- }).should.throw(/Super class test@1.0.0.Order has an explicit identifier sku that cannot be redeclared./);
- });
-
- it('should not allow overriding system identifier', () => {
- const mm = new ModelManager();
-
- (() => {
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset FancyOrder identified {
- o String sku
- }
-
- `, 'test.cto');
- }).should.throw(/Class "FancyOrder" has more than one field named "\$identifier". File 'test.cto': line 4 column 17, to line 6 column 18. /);
- });
-
- it('should not allow overriding explicit identifier with a system identifier', () => {
- const mm = new ModelManager();
-
- (() => {
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset Order identified by sku {
- o Double price
- o String sku
- }
-
- asset FancyOrder identified extends Order {
- o String model
- }
-
- `, 'test.cto');
- }).should.throw(/Super class test@1.0.0.Order has an explicit identifier sku that cannot be redeclared./);
- });
-
- it('should not allow overriding explicit identifier with an explicit identifier', () => {
- const mm = new ModelManager();
-
- (() => {
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset Order identified by sku {
- o Double price
- o String sku
- }
-
- asset FancyOrder identified by model extends Order {
- o String model
- }
-
- `, 'test.cto');
- }).should.throw(/Super class test@1.0.0.Order has an explicit identifier sku that cannot be redeclared./);
- });
-
- it('should not allow field called $identifier', () => {
- const mm = new ModelManager();
-
- (() => {
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset Order {
- o String $identifier
- }`, 'test.cto');
- }).should.throw(/Invalid field name '\$identifier'/);
- });
-
- it('should allow field called $foo', () => {
- const mm = new ModelManager();
-
- mm.addCTOModel( `
- namespace test@1.0.0
-
- asset Order {
- o String $foo
- }`, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- order.getProperties().length.should.equal(2); // XXX Assets always have an identifier
- });
-
- it('should allow abstract assets without an identifier', () => {
- const mm = new ModelManager();
- mm.addCTOModel( `
- namespace test@1.0.0
-
- abstract asset Order {
- o Double price
- }
-
- asset FancyOrder identified by sku extends Order {
- o String sku
- }
- `, 'test.cto');
-
- const order = mm.getType('test@1.0.0.Order');
- order.should.not.be.null;
- order.getProperties().length.should.equal(2); // XXX Assets always have an identifier
- order.isSystemIdentified().should.be.true;
- order.isExplicitlyIdentified().should.be.false;
- });
-
- });
-});
diff --git a/packages/concerto-core/test/introspect/introspector.js b/packages/concerto-core/test/introspect/introspector.js
index 716c6e0d14..7509b2239c 100644
--- a/packages/concerto-core/test/introspect/introspector.js
+++ b/packages/concerto-core/test/introspect/introspector.js
@@ -56,8 +56,8 @@ describe('Introspector', () => {
modelManager.addCTOModel(modelBase, 'model-base.cto');
const introspector = new Introspector(modelManager);
let classDecl = introspector.getClassDeclarations();
- const scalarDecl = classDecl.filter(declaration => declaration.isScalarDeclaration?.());
- const mapDecl = classDecl.filter(declaration => declaration.isMapDeclaration?.());
+ const scalarDecl = classDecl.filter(declaration => declaration.isScalar());
+ const mapDecl = classDecl.filter(declaration => declaration.isMap());
classDecl.length.should.equal(40);
scalarDecl.length.should.equal(0);
mapDecl.length.should.equal(0);
diff --git a/packages/concerto-core/test/introspect/mapdeclaration.js b/packages/concerto-core/test/introspect/mapdeclaration.js
index 5eb78d241b..1d0ee8b0ab 100644
--- a/packages/concerto-core/test/introspect/mapdeclaration.js
+++ b/packages/concerto-core/test/introspect/mapdeclaration.js
@@ -108,39 +108,6 @@ describe('MapDeclaration', () => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodkey.primitive.datetime.cto', MapDeclaration);
decl.validate();
});
-
- it('should throw if invalid $class provided for Map Key', () => {
- (() =>
- {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.BadMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
-
- it('should throw if invalid $class provided for Map Value', () => {
- (() =>
- {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.StringMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.BadMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
});
describe('#validate success scenarios - Map Key', () => {
@@ -280,21 +247,6 @@ describe('MapDeclaration', () => {
describe('#validate failure scenarios - Map Key', () => {
- it('should throw if ast contains illegal Map Key Type', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.UnSupportedMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
it('should throw if ast contains illegal Map Key Type - Concept', () => {
(() => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.badkey.declaration.concept.cto', MapDeclaration);
@@ -330,85 +282,6 @@ describe('MapDeclaration', () => {
});
});
- it('should throw if ast contains illegal Map Key Type - Boolean', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.BooleanMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ast contains illegal Map Key Type - Integer', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.IntegerMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ast contains illegal Map Key Type - Long', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.LongMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ast contains illegal Map Key Type - Double', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.DoubleMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ast contains illegal Map Key Type - Enum', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.EnumMapKeyType',
- type: {
- $class: 'concerto.metamodel@1.0.0.TypeIdentifier',
- name: 'States'
- }
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
it('should throw when map key is imported and is an illegal Map Key Type', () => {
(() => {
const base_cto = fs.readFileSync('test/data/parser/mapdeclaration/base.cto', 'utf-8');
@@ -419,84 +292,6 @@ describe('MapDeclaration', () => {
});
});
- describe('#validate failure scenarios - Map Value', () => {
-
- it('should throw if ObjectMapValueType does not contain type property ', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.StringMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.ObjectMapValueType',
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ObjectMapValueType TypeIdentifier does not contain name property', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.StringMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.ObjectMapValueType',
- type: {
- $class: 'concerto.metamodel@1.0.0.TypeIdentifier',
- }
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ObjectMapValueType TypeIdentifier has bad $class', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.StringMapKeyType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.ObjectMapValueType',
- type: {
- $class: 'concerto.metamodel@1.0.0.BAD_$CLASS',
- name: 'Person',
- }
- }
- });
- }).should.throw(IllegalModelException);
- });
-
- it('should throw if ast contains illegal Map Value Property', () => {
- (() => {
- new MapDeclaration(modelFile, {
- $class: 'concerto.metamodel@1.0.0.MapDeclaration',
- name: 'MapPermutation1',
- key: {
- $class: 'concerto.metamodel@1.0.0.StringMapValueType'
- },
- value: {
- $class: 'concerto.metamodel@1.0.0.EnumMapValueType'
- }
- });
- }).should.throw(IllegalModelException);
- });
-
-
- it('should throw when map value is a map declaration', function() {
- (() => {
- let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.badvalue.declaration.map.cto', MapDeclaration);
- decl.validate();
- }).should.throw(/MapDeclaration as Map Type Value is not supported:/);
- });
- });
-
describe('#accept', () => {
it('should call the visitor', () => {
let clz = new MapDeclaration(modelFile, {
@@ -532,7 +327,7 @@ describe('MapDeclaration', () => {
}
});
(clz.getKey() instanceof MapKeyType).should.be.equal(true);
- clz.getKey().ast.$class.should.equal('concerto.metamodel@1.0.0.StringMapKeyType');
+ clz.getKey().getMetaType().should.equal('concerto.metamodel@1.0.0.StringMapKeyType');
});
it('should return the correct Type when called - String', () => {
@@ -546,7 +341,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.StringMapValueType'
}
});
- clz.getKey().getType().should.equal('String');
+ clz.getKey().getMapKeyType().should.equal('String');
});
it('should return the correct Type when called - DateTime', () => {
@@ -560,18 +355,18 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.StringMapValueType'
}
});
- clz.getKey().getType().should.equal('DateTime');
+ clz.getKey().getMapKeyType().should.equal('DateTime');
});
it('should return the correct Type when called - Scalar DateTime', () => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodkey.scalar.datetime.cto', MapDeclaration);
- decl.getKey().getType().should.equal('DATE');
+ decl.getKey().getMapKeyType().should.equal('DATE');
});
it('should return the correct Type when called - Scalar String', () => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodkey.scalar.string.cto', MapDeclaration);
- decl.getKey().getType().should.equal('GUID');
+ decl.getKey().getMapKeyType().should.equal('GUID');
});
it('should return the correct boolean value introspecting isValue or isKey', () => {
@@ -585,8 +380,8 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.DoubleMapValueType'
}
});
- expect(clz.getKey().isKey()).to.be.true;
- expect(clz.getKey().isValue()).to.be.false;
+ expect(clz.getKey().isMapKey()).to.be.true;
+ expect(clz.getKey().isMapValue()).to.be.false;
});
});
@@ -603,7 +398,7 @@ describe('MapDeclaration', () => {
}
});
(clz.getValue() instanceof MapValueType).should.be.equal(true);
- clz.getValue().ast.$class.should.equal('concerto.metamodel@1.0.0.StringMapValueType');
+ clz.getValue().getMetaType().should.equal('concerto.metamodel@1.0.0.StringMapValueType');
});
it('should return the correct Type when called - Boolean', () => {
@@ -617,7 +412,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.BooleanMapValueType'
}
});
- clz.getValue().getType().should.equal('Boolean');
+ clz.getValue().getMapValueType().should.equal('Boolean');
});
it('should return the correct Type when called - DateTime', () => {
@@ -631,7 +426,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.DateTimeMapValueType'
}
});
- clz.getValue().getType().should.equal('DateTime');
+ clz.getValue().getMapValueType().should.equal('DateTime');
});
it('should return the correct Type when called - String', () => {
@@ -645,7 +440,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.StringMapValueType'
}
});
- clz.getValue().getType().should.equal('String');
+ clz.getValue().getMapValueType().should.equal('String');
});
it('should return the correct Type when called - Integer', () => {
@@ -659,7 +454,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.IntegerMapValueType'
}
});
- clz.getValue().getType().should.equal('Integer');
+ clz.getValue().getMapValueType().should.equal('Integer');
});
it('should return the correct Type when called - Long', () => {
@@ -673,7 +468,7 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.LongMapValueType'
}
});
- clz.getValue().getType().should.equal('Long');
+ clz.getValue().getMapValueType().should.equal('Long');
});
it('should return the correct Type when called - Double', () => {
@@ -687,17 +482,17 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.DoubleMapValueType'
}
});
- clz.getValue().getType().should.equal('Double');
+ clz.getValue().getMapValueType().should.equal('Double');
});
it('should return the correct values when called - Scalar DateTime', () => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodvalue.scalar.datetime.cto', MapDeclaration);
- decl.getValue().getType().should.equal('DATE');
+ decl.getValue().getMapValueType().should.equal('DATE');
});
it('should return the correct values when called - Scalar String', () => {
let decl = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodvalue.scalar.string.cto', MapDeclaration);
- decl.getValue().getType().should.equal('GUID');
+ decl.getValue().getMapValueType().should.equal('GUID');
});
it('should return the correct boolean value introspecting isValue or isKey', () => {
@@ -711,8 +506,8 @@ describe('MapDeclaration', () => {
$class: 'concerto.metamodel@1.0.0.DoubleMapValueType'
}
});
- expect(clz.getValue().isValue()).to.be.true;
- expect(clz.getValue().isKey()).to.be.false;
+ expect(clz.getValue().isMapValue()).to.be.true;
+ expect(clz.getValue().isMapKey()).to.be.false;
});
});
@@ -737,16 +532,7 @@ describe('MapDeclaration', () => {
let declaration = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodkey.primitive.string.cto', MapDeclaration);
declaration.declarationKind().should.equal('MapDeclaration');
declaration.getFullyQualifiedName().should.equal('com.acme@1.0.0.Dictionary');
- declaration.isMapDeclaration().should.equal(true);
- });
- });
-
- describe('#toString', () => {
- it('should give the correct value for Map Declaration', () => {
- let declaration = introspectUtils.loadLastDeclaration('test/data/parser/mapdeclaration/mapdeclaration.goodkey.primitive.string.cto', MapDeclaration);
- declaration.toString().should.equal('MapDeclaration {id=com.acme@1.0.0.Dictionary}');
- declaration.getKey().toString().should.equal('MapKeyType {id=String}');
- declaration.getValue().toString().should.equal('MapValueType {id=String}');
+ declaration.isMap().should.equal(true);
});
});
diff --git a/packages/concerto-core/test/introspect/modelfile.js b/packages/concerto-core/test/introspect/modelfile.js
index 52fd777689..469af7d5d9 100644
--- a/packages/concerto-core/test/introspect/modelfile.js
+++ b/packages/concerto-core/test/introspect/modelfile.js
@@ -60,7 +60,7 @@ describe('ModelFile', () => {
it('should throw when null ast provided', () => {
(() => {
new ModelFile(modelManager, null);
- }).should.throw(/ast not specified/);
+ }).should.throw(/ModelFile expects a Concerto model AST as input./);
});
it('should throw when non object ast provided', () => {
diff --git a/packages/concerto-core/test/introspect/property.js b/packages/concerto-core/test/introspect/property.js
index 53e3ad0e5b..14fbc72de0 100644
--- a/packages/concerto-core/test/introspect/property.js
+++ b/packages/concerto-core/test/introspect/property.js
@@ -42,7 +42,7 @@ describe('Property', () => {
$class: `${MetaModelNamespace}.StringProperty`,
name: null
});
- }).should.throw(/No name for type/);
+ }).should.throw(/Property of type String must have a name/);
});
it('should not throw for an identifier named null', () => {
@@ -62,7 +62,7 @@ describe('Property', () => {
name: 'suchType',
}
});
- p.type.should.equal('suchType');
+ p.getPropertyType().should.equal('suchType');
});
it('should handle a missing incoming property type', () => {
@@ -70,7 +70,7 @@ describe('Property', () => {
$class: `${MetaModelNamespace}.ObjectProperty`,
name: 'property',
});
- should.equal(p.type, null);
+ should.equal(p.getPropertyType(), null);
});
it('should not be an array by default', () => {
@@ -96,7 +96,7 @@ describe('Property', () => {
$class: `${MetaModelNamespace}.StringProperty`,
name: '1st',
});
- }).should.throw(/Invalid property name '1st'/);
+ }).should.throw(/Invalid model element name '1st'/);
});
});
diff --git a/packages/concerto-core/test/introspect/property_ex.js b/packages/concerto-core/test/introspect/property_ex.js
index 071b29a1a9..9f24ec838d 100644
--- a/packages/concerto-core/test/introspect/property_ex.js
+++ b/packages/concerto-core/test/introspect/property_ex.js
@@ -83,7 +83,7 @@ describe('Property', function () {
it('toString works', function () {
const person = modelManager.getType('org.acme.l1@1.0.0.Car');
const field = person.getProperty('owner');
- field.toString().should.equal('RelationshipDeclaration {name=owner, type=org.acme.l1@1.0.0.Person, array=false, optional=false}');
+ field.toString().should.equal('RelationshipProperty {name=owner, type=org.acme.l1@1.0.0.Person, array=false, optional=false}');
});
});
});
diff --git a/packages/concerto-core/test/introspect/relationshipdeclaration.js b/packages/concerto-core/test/introspect/relationshipproperty.js
similarity index 89%
rename from packages/concerto-core/test/introspect/relationshipdeclaration.js
rename to packages/concerto-core/test/introspect/relationshipproperty.js
index cc25105bc1..6dbcceab33 100644
--- a/packages/concerto-core/test/introspect/relationshipdeclaration.js
+++ b/packages/concerto-core/test/introspect/relationshipproperty.js
@@ -17,14 +17,14 @@
const ModelManager = require('../../src/modelmanager');
const sinon = require('sinon');
const ClassDeclaration = require('../../src/introspect/classdeclaration');
-const RelationshipDeclaration = require('../../src/introspect/relationshipdeclaration');
+const RelationshipProperty = require('../../src/introspect/relationshipproperty');
const Util = require('../composer/composermodelutility');
const chai = require('chai');
chai.should();
chai.use(require('chai-things'));
-describe('RelationshipDeclaration', function () {
+describe('RelationshipProperty', function () {
let modelManager;
let mockClassDeclaration;
@@ -52,9 +52,9 @@ describe('RelationshipDeclaration', function () {
modelManager.addCTOModel(levelOneModel);
const vehicleDeclaration = modelManager.getType('org.acme.l1@1.0.0.Car');
const field = vehicleDeclaration.getProperty('owner');
- (field instanceof RelationshipDeclaration).should.be.true;
+ (field instanceof RelationshipProperty).should.be.true;
// stub the getType method to return null
- sinon.stub(field, 'getType').callsFake(function(){return null;});
+ sinon.stub(field, 'getPropertyType').callsFake(function(){return null;});
(function () {
field.validate(vehicleDeclaration);
}).should.throw(/Relationship must have a type/);
@@ -81,7 +81,7 @@ describe('RelationshipDeclaration', function () {
modelManager.addCTOModel(model2);
const vehicleDeclaration = modelManager.getType('org.acme.l2@1.0.0.Car');
const field = vehicleDeclaration.getProperty('owner');
- (field instanceof RelationshipDeclaration).should.be.true;
+ (field instanceof RelationshipProperty).should.be.true;
modelManager.getType = () => { return null; };
(function () {
@@ -104,7 +104,7 @@ describe('RelationshipDeclaration', function () {
modelManager.addCTOModel(model);
const vehicleDeclaration = modelManager.getType('org.acme.l1@1.0.0.Car');
const field = vehicleDeclaration.getProperty('owner');
- (field instanceof RelationshipDeclaration).should.be.true;
+ (field instanceof RelationshipProperty).should.be.true;
field.getParent().getModelFile().getType = () => {return mockClassDeclaration;};
(function () {
diff --git a/packages/concerto-core/test/introspect/scalardeclaration.js b/packages/concerto-core/test/introspect/scalardeclaration.js
index a653f39612..cf7e38dd95 100644
--- a/packages/concerto-core/test/introspect/scalardeclaration.js
+++ b/packages/concerto-core/test/introspect/scalardeclaration.js
@@ -14,6 +14,8 @@
'use strict';
+const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
+
const ScalarDeclaration = require('../../src/introspect/scalardeclaration');
const IntrospectUtils = require('./introspectutils');
const ParserUtil = require('./parserutility');
@@ -42,13 +44,14 @@ describe('ScalarDeclaration', () => {
let asset = introspectUtils.loadLastDeclaration('test/data/parser/scalardeclaration.dupeboolean.cto', ScalarDeclaration);
(() => {
asset.validate();
- }).should.throw(/Duplicate class/);
+ }).should.throw(/Duplicate declaration/);
});
});
describe('#accept', () => {
it('should call the visitor', () => {
let clz = new ScalarDeclaration(modelFile, {
+ $class: `${MetaModelNamespace}.StringScalar`,
name: 'suchName'
});
let visitor = {
@@ -64,10 +67,11 @@ describe('ScalarDeclaration', () => {
describe('#getName', () => {
it('should return the scalar name', () => {
let clz = new ScalarDeclaration(modelFile, {
- name: 'suchName'
+ name: 'suchName',
+ $class: `${MetaModelNamespace}.BooleanScalar`
});
clz.getName().should.equal('suchName');
- clz.toString().should.equal('ScalarDeclaration {id=com.hyperledger.testing@1.0.0.suchName}');
+ clz.toString().should.equal('ScalarDeclaration {id=com.hyperledger.testing@1.0.0.suchName scalarType=Boolean}');
});
});
@@ -89,38 +93,13 @@ describe('ScalarDeclaration', () => {
it('should return the fully qualified name if function is in a namespace', () => {
let clz = new ScalarDeclaration(modelFile, {
name: 'suchName',
+ $class: `${MetaModelNamespace}.StringScalar`,
});
clz.getFullyQualifiedName().should.equal('com.hyperledger.testing@1.0.0.suchName');
});
});
- describe('#getSuperType', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return null', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- should.equal(testClass.getSuperType(), null);
- });
- });
-
- describe('#getSuperTypeDeclaration', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return null', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- should.equal(testClass.getSuperTypeDeclaration(), null);
- });
- });
-
describe('#getValidator', () => {
const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
@@ -150,19 +129,6 @@ describe('ScalarDeclaration', () => {
});
});
- describe('#isIdentified', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return false', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- testClass.isIdentified().should.be.false;
- });
- });
-
describe('#isAsset', () => {
const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
@@ -176,45 +142,6 @@ describe('ScalarDeclaration', () => {
});
});
- describe('#isSystemIdentified', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return false', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- testClass.isSystemIdentified().should.be.false;
- });
- });
-
- describe('#getIdentifierFieldName', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return null', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- should.equal(testClass.getIdentifierFieldName(), null);
- });
- });
-
- describe('#isAbstract', () => {
- const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
-
- beforeEach(() => {
- const modelFiles = introspectUtils.loadModelFiles([modelFileName], modelManager);
- modelManager.addModelFiles(modelFiles);
- });
- it('should return true', () => {
- const testClass = modelManager.getType('com.testing@1.0.0.SSN');
- testClass.isAbstract().should.be.true;
- });
- });
-
describe('#isAsset', () => {
const modelFileName = 'test/data/parser/scalardeclaration.ssn.cto';
diff --git a/packages/concerto-core/test/model/concept.js b/packages/concerto-core/test/model/concept.js
index 6df2e41dcc..0ac5c8ce2f 100644
--- a/packages/concerto-core/test/model/concept.js
+++ b/packages/concerto-core/test/model/concept.js
@@ -42,7 +42,7 @@ describe('Concept', function () {
let classDecl = null;
before(function () {
- modelManager = new ModelManager();
+ modelManager = new ModelManager( {enableMapType: true} );
Util.addComposerModel(modelManager);
});
@@ -140,7 +140,6 @@ describe('Concept', function () {
invCount:10,
invType:'NEWBATCH',
dictionary: {
- $class: 'org.acme.biznet@1.0.0.Dictionary',
key1: 'value1',
key2: 'value2',
}
diff --git a/packages/concerto-core/test/models/animaltracking.js b/packages/concerto-core/test/models/animaltracking.js
index 0a8f445018..1b73aa6dd8 100644
--- a/packages/concerto-core/test/models/animaltracking.js
+++ b/packages/concerto-core/test/models/animaltracking.js
@@ -19,7 +19,7 @@ require('chai').should();
const Factory = require('../../src/factory');
const ModelManager = require('../../src/modelmanager');
const Relationship = require('../../src/model/relationship');
-const RelationshipDeclaration = require('../../src/introspect/relationshipdeclaration');
+const RelationshipProperty = require('../../src/introspect/relationshipproperty');
const Serializer = require('../../src/serializer');
const fs = require('fs');
const Util = require('../composer/composermodelutility');
@@ -77,7 +77,7 @@ describe('animaltracking Model', function(){
incomingAnimals.should.not.be.null;
incomingAnimals.getType().should.equal('Animal');
incomingAnimals.isArray().should.be.true;
- (incomingAnimals instanceof RelationshipDeclaration).should.be.true;
+ (incomingAnimals instanceof RelationshipProperty).should.be.true;
});
it('create and serialize instance', function() {
diff --git a/packages/concerto-core/test/models/farm2fork.js b/packages/concerto-core/test/models/farm2fork.js
index 4b933e1567..0d2843aa38 100644
--- a/packages/concerto-core/test/models/farm2fork.js
+++ b/packages/concerto-core/test/models/farm2fork.js
@@ -16,7 +16,7 @@
require('chai').should();
const ModelManager = require('../../src/modelmanager');
-const RelationshipDeclaration = require('../../src/introspect/relationshipdeclaration');
+const RelationshipProperty = require('../../src/introspect/relationshipproperty');
const Util = require('../composer/composermodelutility');
const fs = require('fs');
@@ -67,7 +67,7 @@ describe('Farm2Fork Model', function() {
let previousKeeperField = animal.getProperty('previousKeeper');
previousKeeperField.getName().should.equal('previousKeeper');
previousKeeperField.isArray().should.be.true;
- (previousKeeperField instanceof RelationshipDeclaration).should.be.true;
+ (previousKeeperField instanceof RelationshipProperty).should.be.true;
previousKeeperField.getType().should.equal('MyParticipant');
// test the VehicleTransferredToScrapMerchant class
@@ -84,7 +84,7 @@ describe('Farm2Fork Model', function() {
let animalField = txDecl.getProperty('animal');
animalField.should.not.be.null;
animalField.getType().should.equal('Animal');
- (animalField instanceof RelationshipDeclaration).should.be.true;
+ (animalField instanceof RelationshipProperty).should.be.true;
});
});
});
diff --git a/packages/concerto-core/test/models/test.js b/packages/concerto-core/test/models/test.js
index 6bbb9bd20d..701eeb45c9 100644
--- a/packages/concerto-core/test/models/test.js
+++ b/packages/concerto-core/test/models/test.js
@@ -16,7 +16,7 @@
const Factory = require('../../src/factory');
const ModelManager = require('../../src/modelmanager');
-const RelationshipDeclaration = require('../../src/introspect/relationshipdeclaration');
+const RelationshipProperty = require('../../src/introspect/relationshipproperty');
const Serializer = require('../../src/serializer');
const TypeNotFoundException = require('../../src/typenotfoundexception');
const fs = require('fs');
@@ -294,7 +294,7 @@ describe('Test Model', function(){
// Nary relationship
let previousOwnersField = vehicle.getProperty('previousOwners');
previousOwnersField.isArray().should.be.true;
- (previousOwnersField instanceof RelationshipDeclaration).should.be.true;
+ (previousOwnersField instanceof RelationshipProperty).should.be.true;
previousOwnersField.getType().should.equal('MyParticipant');
// test the VehicleTransferredToScrapMerchant class
@@ -311,7 +311,7 @@ describe('Test Model', function(){
let vehicleField = txDecl.getProperty('vehicle');
vehicleField.should.not.be.null;
vehicleField.getType().should.equal('Vehicle');
- (vehicleField instanceof RelationshipDeclaration).should.be.true;
+ (vehicleField instanceof RelationshipProperty).should.be.true;
});
});
diff --git a/packages/concerto-core/test/serializer/instancegenerator.js b/packages/concerto-core/test/serializer/instancegenerator.js
index fe5f3b2239..75bc9c5fa8 100644
--- a/packages/concerto-core/test/serializer/instancegenerator.js
+++ b/packages/concerto-core/test/serializer/instancegenerator.js
@@ -42,7 +42,7 @@ describe('InstanceGenerator', () => {
};
beforeEach(() => {
- modelManager = new ModelManager();
+ modelManager = new ModelManager({enableMapType: true});
Util.addComposerModel(modelManager);
factory = new Factory(modelManager);
parameters = {
@@ -67,7 +67,7 @@ describe('InstanceGenerator', () => {
it('should throw on unrecognized thing', () => {
(() => {
visitor.visit(dayjs.utc(), {});
- }).should.throw(/Unrecognised/);
+ }).should.throw(/Model element is invalid/);
});
it('should generate a default value for a string property', () => {
@@ -111,7 +111,7 @@ describe('InstanceGenerator', () => {
values[1].should.be.a('string');
});
- it('should generate a value with specified lentgh constraint for a string property', () => {
+ it('should generate a value with specified length constraint for a string property', () => {
useSampleGenerator();
let resource = test(`namespace org.acme.test@1.0.0
asset MyAsset identified by assetId {
diff --git a/packages/concerto-core/test/serializer/jsongenerator.js b/packages/concerto-core/test/serializer/jsongenerator.js
index a74b8d7e7c..1f4fabaa98 100644
--- a/packages/concerto-core/test/serializer/jsongenerator.js
+++ b/packages/concerto-core/test/serializer/jsongenerator.js
@@ -121,7 +121,7 @@ describe('JSONGenerator', () => {
it('should throw an error for an unrecognized type', () => {
(() => {
jsonGenerator.visit(3.142, {});
- }).should.throw(/Unrecognised/);
+ }).should.throw(/Model element is invalid/);
});
});
diff --git a/packages/concerto-core/test/serializer/jsonpopulator.js b/packages/concerto-core/test/serializer/jsonpopulator.js
index b875376327..ac131684db 100644
--- a/packages/concerto-core/test/serializer/jsonpopulator.js
+++ b/packages/concerto-core/test/serializer/jsonpopulator.js
@@ -95,7 +95,7 @@ describe('JSONPopulator', () => {
it('should throw an error for an unrecognized type', () => {
(() => {
jsonPopulator.visit(3.142, {});
- }).should.throw(/Unrecognised/);
+ }).should.throw(/Model element is invalid/);
});
});
diff --git a/packages/concerto-core/test/serializer/maptype/serializer.js b/packages/concerto-core/test/serializer/maptype/serializer.js
index 44a5a5f7b5..9ed9f53a30 100644
--- a/packages/concerto-core/test/serializer/maptype/serializer.js
+++ b/packages/concerto-core/test/serializer/maptype/serializer.js
@@ -14,6 +14,8 @@
'use strict';
+const dayjs = require('dayjs');
+
const Factory = require('../../../src/factory');
const ModelManager = require('../../../src/modelmanager');
const Resource = require('../../../src/model/resource');
@@ -292,8 +294,8 @@ describe('Serializer', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
concept.birthday = new Map();
- concept.birthday.set('Bob', '2023-10-28T01:02:03Z');
- concept.birthday.set('Alice', '2024-10-28T01:02:03Z');
+ concept.birthday.set('Bob', dayjs('2023-10-28T01:02:03Z'));
+ concept.birthday.set('Alice', dayjs('2024-10-28T01:02:03Z'));
// serialize and assert
const json = serializer.toJSON(concept);
@@ -301,8 +303,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
birthday: {
- Bob: '2023-10-28T01:02:03Z',
- Alice: '2024-10-28T01:02:03Z'
+ Bob: '2023-10-28T01:02:03.000Z',
+ Alice: '2024-10-28T01:02:03.000Z'
}
});
@@ -311,8 +313,8 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.birthday.should.be.an.instanceOf(Map);
- resource.birthday.get('Bob').should.equal('2023-10-28T01:02:03Z');
- resource.birthday.get('Alice').should.equal('2024-10-28T01:02:03Z');
+ resource.birthday.get('Bob').unix().should.equal(dayjs('2023-10-28T01:02:03Z').unix());
+ resource.birthday.get('Alice').unix().should.equal(dayjs('2024-10-28T01:02:03Z').unix());
});
it('should serialize -> deserialize with a Map ', () => {
@@ -320,8 +322,8 @@ describe('Serializer', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
concept.celebration = new Map();
- concept.celebration.set('BobBirthday', '2022-11-28T01:02:03Z');
- concept.celebration.set('AliceAnniversary', '2023-10-28T01:02:03Z');
+ concept.celebration.set('BobBirthday', dayjs('2022-11-28T01:02:03Z'));
+ concept.celebration.set('AliceAnniversary', dayjs('2023-10-28T01:02:03Z'));
// serialize and assert
const json = serializer.toJSON(concept);
@@ -329,8 +331,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
celebration: {
- 'BobBirthday': '2022-11-28T01:02:03Z',
- 'AliceAnniversary': '2023-10-28T01:02:03Z',
+ 'BobBirthday': '2022-11-28T01:02:03.000Z',
+ 'AliceAnniversary': '2023-10-28T01:02:03.000Z',
}
});
@@ -339,8 +341,8 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.celebration.should.be.an.instanceOf(Map);
- resource.celebration.get('BobBirthday').should.equal('2022-11-28T01:02:03Z');
- resource.celebration.get('AliceAnniversary').should.equal('2023-10-28T01:02:03Z');
+ resource.celebration.get('BobBirthday').unix().should.equal(dayjs('2022-11-28T01:02:03Z').unix());
+ resource.celebration.get('AliceAnniversary').unix().should.equal(dayjs('2023-10-28T01:02:03Z').unix());
});
it('should serialize -> deserialize with a Map ', () => {
@@ -383,8 +385,8 @@ describe('Serializer', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
concept.appointment = new Map();
- concept.appointment.set('2023-11-28T01:02:03Z', 'BobBirthday');
- concept.appointment.set('2024-10-28T01:02:03Z', 'AliceAnniversary');
+ concept.appointment.set(dayjs('2023-11-28T01:02:03Z'), 'BobBirthday');
+ concept.appointment.set(dayjs('2024-10-28T01:02:03Z'), 'AliceAnniversary');
// serialize and assert
const json = serializer.toJSON(concept);
@@ -392,8 +394,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
appointment: {
- '2023-11-28T01:02:03Z': 'BobBirthday',
- '2024-10-28T01:02:03Z': 'AliceAnniversary'
+ '2023-11-28T01:02:03.000Z': 'BobBirthday',
+ '2024-10-28T01:02:03.000Z': 'AliceAnniversary'
}
});
@@ -402,8 +404,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.appointment.should.be.an.instanceOf(Map);
- resource.appointment.get('2023-11-28T01:02:03Z').should.equal('BobBirthday');
- resource.appointment.get('2024-10-28T01:02:03Z').should.equal('AliceAnniversary');
+ const keyIt = resource.appointment.keys();
+ resource.appointment.get(keyIt.next().value).should.equal('BobBirthday');
+ resource.appointment.get(keyIt.next().value).should.equal('AliceAnniversary');
});
it('should serialize -> deserialize with a Map : Scalar extends String', () => {
@@ -439,8 +442,8 @@ describe('Serializer', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
concept.stopwatch = new Map();
- concept.stopwatch.set('2023-10-28T00:00:00Z', '2023-10-28T11:12:13Z');
- concept.stopwatch.set('2024-11-28T00:00:00Z', '2024-11-28T11:12:13Z');
+ concept.stopwatch.set(dayjs('2023-10-28T00:00:00Z'), dayjs('2023-10-28T11:12:13Z'));
+ concept.stopwatch.set(dayjs('2024-11-28T00:00:00Z'), dayjs('2024-11-28T11:12:13Z'));
// serialize and assert
const json = serializer.toJSON(concept);
@@ -448,8 +451,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
stopwatch: {
- '2023-10-28T00:00:00Z': '2023-10-28T11:12:13Z',
- '2024-11-28T00:00:00Z': '2024-11-28T11:12:13Z',
+ '2023-10-28T00:00:00.000Z': '2023-10-28T11:12:13.000Z',
+ '2024-11-28T00:00:00.000Z': '2024-11-28T11:12:13.000Z',
}
});
@@ -458,8 +461,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.stopwatch.should.be.an.instanceOf(Map);
- resource.stopwatch.get('2023-10-28T00:00:00Z').should.equal('2023-10-28T11:12:13Z');
- resource.stopwatch.get('2024-11-28T00:00:00Z').should.equal('2024-11-28T11:12:13Z');
+ const keyIt = resource.stopwatch.keys();
+ resource.stopwatch.get(keyIt.next().value).unix().should.equal(dayjs('2023-10-28T11:12:13Z').unix());
+ resource.stopwatch.get(keyIt.next().value).unix().should.equal(dayjs('2024-11-28T11:12:13Z').unix());
});
it('should serialize -> deserialize with a Map ', () => {
@@ -503,8 +507,8 @@ describe('Serializer', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
concept.diary = new Map();
- concept.diary.set('2023-10-28T01:02:03Z', 'Birthday');
- concept.diary.set('2024-10-28T01:02:03Z', 'Anniversary');
+ concept.diary.set(dayjs('2023-10-28T01:02:03Z'), 'Birthday');
+ concept.diary.set(dayjs('2024-10-28T01:02:03Z'), 'Anniversary');
// serialize and assert
const json = serializer.toJSON(concept);
@@ -512,8 +516,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
diary: {
- '2023-10-28T01:02:03Z': 'Birthday',
- '2024-10-28T01:02:03Z': 'Anniversary'
+ '2023-10-28T01:02:03.000Z': 'Birthday',
+ '2024-10-28T01:02:03.000Z': 'Anniversary'
}
});
@@ -522,8 +526,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.diary.should.be.an.instanceOf(Map);
- resource.diary.get('2023-10-28T01:02:03Z').should.equal('Birthday');
- resource.diary.get('2024-10-28T01:02:03Z').should.equal('Anniversary');
+ const keyIt = resource.diary.keys();
+ resource.diary.get(keyIt.next().value).should.equal('Birthday');
+ resource.diary.get(keyIt.next().value).should.equal('Anniversary');
});
});
@@ -679,12 +684,16 @@ describe('Serializer', () => {
});
it('should deserialize -> serialize with a Map ', () => {
+
+ const bobBirthday = dayjs('2023-10-28T01:02:03Z');
+ const aliceBirthday = dayjs('2024-10-28T01:02:03Z');
+
// setup
let json = {
$class: 'org.acme.sample@1.0.0.Concepts',
birthday: {
- Bob: '2023-10-28T01:02:03Z',
- Alice: '2024-10-28T01:02:03Z'
+ Bob: bobBirthday.utc(),
+ Alice: aliceBirthday.utc()
}
};
@@ -693,8 +702,8 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.birthday.should.be.an.instanceOf(Map);
- resource.birthday.get('Bob').should.equal('2023-10-28T01:02:03Z');
- resource.birthday.get('Alice').should.equal('2024-10-28T01:02:03Z');
+ resource.birthday.get('Bob').unix().should.equal(bobBirthday.unix());
+ resource.birthday.get('Alice').unix().should.equal(aliceBirthday.unix());
// serialize and assert
json = serializer.toJSON(resource);
@@ -702,19 +711,23 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
birthday: {
- Bob: '2023-10-28T01:02:03Z',
- Alice: '2024-10-28T01:02:03Z'
+ Bob: '2023-10-28T01:02:03.000Z',
+ Alice: '2024-10-28T01:02:03.000Z'
}
});
});
it('should deserialize -> serialize with a Map ', () => {
+
+ const bobBirthday = dayjs('2022-11-28T01:02:03.000Z');
+ const aliceAnniversary = dayjs('2023-10-28T01:02:03.000Z');
+
// setup
let json = {
$class: 'org.acme.sample@1.0.0.Concepts',
celebration: {
- 'BobBirthday': '2022-11-28T01:02:03Z',
- 'AliceAnniversary': '2023-10-28T01:02:03Z',
+ 'BobBirthday': bobBirthday.utc(),
+ 'AliceAnniversary': aliceAnniversary.utc(),
}
};
@@ -723,8 +736,8 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.celebration.should.be.an.instanceOf(Map);
- resource.celebration.get('BobBirthday').should.equal('2022-11-28T01:02:03Z');
- resource.celebration.get('AliceAnniversary').should.equal('2023-10-28T01:02:03Z');
+ resource.celebration.get('BobBirthday').unix().should.equal(bobBirthday.unix());
+ resource.celebration.get('AliceAnniversary').unix().should.equal(aliceAnniversary.unix());
// serialize and assert
json = serializer.toJSON(resource);
@@ -732,8 +745,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
celebration: {
- 'BobBirthday': '2022-11-28T01:02:03Z',
- 'AliceAnniversary': '2023-10-28T01:02:03Z',
+ 'BobBirthday': '2022-11-28T01:02:03.000Z',
+ 'AliceAnniversary': '2023-10-28T01:02:03.000Z',
}
});
@@ -788,8 +801,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.appointment.should.be.an.instanceOf(Map);
- resource.appointment.get('2023-11-28T01:02:03Z').should.equal('Lorem');
- resource.appointment.get('2024-10-28T01:02:03Z').should.equal('Ipsum');
+ const keyIt = resource.appointment.keys();
+ resource.appointment.get(keyIt.next().value).should.equal('Lorem');
+ resource.appointment.get(keyIt.next().value).should.equal('Ipsum');
// serialize & assert
json = serializer.toJSON(resource);
@@ -797,8 +811,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
appointment: {
- '2023-11-28T01:02:03Z': 'Lorem',
- '2024-10-28T01:02:03Z': 'Ipsum'
+ '2023-11-28T01:02:03.000Z': 'Lorem',
+ '2024-10-28T01:02:03.000Z': 'Ipsum'
}
});
});
@@ -848,8 +862,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.stopwatch.should.be.an.instanceOf(Map);
- resource.stopwatch.get('2023-10-28T00:00:00Z').should.equal('2023-10-28T11:12:13Z');
- resource.stopwatch.get('2024-11-28T00:00:00Z').should.equal('2024-11-28T11:12:13Z');
+ const keyIt = resource.stopwatch.keys();
+ resource.stopwatch.get(keyIt.next().value).unix().should.equal(dayjs('2023-10-28T11:12:13Z').unix());
+ resource.stopwatch.get(keyIt.next().value).unix().should.equal(dayjs('2024-11-28T11:12:13Z').unix());
// serialize & assert
json = serializer.toJSON(resource);
@@ -857,8 +872,8 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
stopwatch: {
- '2023-10-28T00:00:00Z': '2023-10-28T11:12:13Z',
- '2024-11-28T00:00:00Z': '2024-11-28T11:12:13Z',
+ '2023-10-28T00:00:00.000Z': '2023-10-28T11:12:13.000Z',
+ '2024-11-28T00:00:00.000Z': '2024-11-28T11:12:13.000Z',
}
});
});
@@ -910,8 +925,9 @@ describe('Serializer', () => {
resource.should.be.an.instanceOf(Resource);
resource.diary.should.be.an.instanceOf(Map);
- resource.diary.get('2023-10-28T01:02:03Z').should.equal('Birthday');
- resource.diary.get('2024-10-28T01:02:03Z').should.equal('Anniversary');
+ const keyIt = resource.diary.keys();
+ resource.diary.get(keyIt.next().value).should.equal('Birthday');
+ resource.diary.get(keyIt.next().value).should.equal('Anniversary');
// serialize and assert
json = serializer.toJSON(resource);
@@ -919,80 +935,14 @@ describe('Serializer', () => {
json.should.deep.equal({
$class: 'org.acme.sample@1.0.0.Concepts',
diary: {
- '2023-10-28T01:02:03Z': 'Birthday',
- '2024-10-28T01:02:03Z': 'Anniversary'
+ '2023-10-28T01:02:03.000Z': 'Birthday',
+ '2024-10-28T01:02:03.000Z': 'Anniversary'
}
});
});
});
describe('#toJSON failure scenarios', () => {
- it('should throw if bad Key value is provided for Map, where Key Type DateTime is expected', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.appointment = new Map();
- concept.appointment.set('BAD-DATE-28T01:02:03Z', 'Lorem'); // Bad DateTime
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw('Model violation in org.acme.sample@1.0.0.Appointment. Expected Type of DateTime but found \'BAD-DATE-28T01:02:03Z\' instead.');
- });
-
- it('should throw if bad Key value is provided for Map, where Key Type String is expected', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.dict = new Map();
- concept.dict.set(1234, 'Lorem'); // Bad key
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw('Model violation in org.acme.sample@1.0.0.Dictionary. Expected Type of String but found \'1234\' instead.');
- });
-
- it('should throw if a bad Value is Supplied for Map, where Value type Boolean is expected', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.rsvp = new Map();
- concept.rsvp.set('Lorem', true);
- concept.rsvp.set('Ipsum', 'false');
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw('Model violation in org.acme.sample@1.0.0.RSVP. Expected Type of Boolean but found string instead, for value \'false\'.');
- });
-
- it('should throw if a bad Value is Supplied for Map, where Value type String is expected', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.dict = new Map();
- concept.dict.set('Lorem', 1234);
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw('Model violation in org.acme.sample@1.0.0.Dictionary. Expected Type of String but found \'1234\' instead.');
- });
-
- it('should throw if a bad value is Supplied for Map - where Value type Boolean is expected', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.timer = new Map();
- concept.timer.set('2023-10-28T01:02:03Z', '2023-10-28T01:02:03Z');
- concept.timer.set('2023-10-28T01:02:03Z', 'BAD-DATE-VALUE');
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw('Model violation in org.acme.sample@1.0.0.Timer. Expected Type of DateTime but found \'BAD-DATE-VALUE\' instead.');
- });
-
- it('should throw if the value of a Map is not a Map instance', () => {
- let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
-
- concept.dict = 'xyz'; // bad value
-
- (() => {
- serializer.toJSON(concept);
- }).should.throw(`Expected a Map, but found ${JSON.stringify(concept.dict)}`);
- });
it('should ignore system properties', () => {
let concept = factory.newConcept('org.acme.sample@1.0.0', 'Concepts');
diff --git a/packages/concerto-core/test/serializer/resourcevalidator.js b/packages/concerto-core/test/serializer/resourcevalidator.js
index 515fda3d9f..4ad0840066 100644
--- a/packages/concerto-core/test/serializer/resourcevalidator.js
+++ b/packages/concerto-core/test/serializer/resourcevalidator.js
@@ -117,7 +117,7 @@ describe('ResourceValidator', function () {
before(function () {
sandbox = sinon.createSandbox();
resourceValidator = new ResourceValidator();
- modelManager = new ModelManager();
+ modelManager = new ModelManager({enableMapType: true});
Util.addComposerModel(modelManager);
factory = new Factory(modelManager);
});
@@ -135,32 +135,6 @@ describe('ResourceValidator', function () {
sandbox.restore();
});
- describe('#visit', () => {
- it('should do nothing if unknown object given', () => {
- const parameters = {
- stack: new TypedStack({})
- };
-
- const thing = {
- toString: () => {
- return 'testing';
- }
- };
- sandbox.stub(resourceValidator, 'visitEnumDeclaration');
- sandbox.stub(resourceValidator, 'visitClassDeclaration');
- sandbox.stub(resourceValidator, 'visitRelationshipDeclaration');
- sandbox.stub(resourceValidator, 'visitField');
-
- resourceValidator.visit(thing, parameters);
-
- sinon.assert.notCalled(resourceValidator.visitEnumDeclaration);
- sinon.assert.notCalled(resourceValidator.visitClassDeclaration);
- sinon.assert.notCalled(resourceValidator.visitRelationshipDeclaration);
- sinon.assert.notCalled(resourceValidator.visitField);
-
- });
- });
-
describe('#visitRelationshipDeclaration', function() {
it('should detect assigning a resource to a relationship', function () {
const employee = factory.newResource('org.acme.l1@1.0.0', 'Employee', 'DAN');
@@ -348,7 +322,7 @@ describe('ResourceValidator', function () {
describe('#visitMapDeclaration', function() {
it('should validate map', function () {
- const map = new Map([['$class', 'org.acme.map@1.0.0.PhoneBook'], ['Lorem', 'Ipsum']]);
+ const map = new Map([['Lorem', 'Ipsum']]);
const typedStack = new TypedStack(map);
const mapDeclaration = modelManager.getType('org.acme.map@1.0.0.PhoneBook');
const parameters = { stack : typedStack, 'modelManager' : modelManager, rootResourceIdentifier : 'TEST' };
@@ -356,25 +330,25 @@ describe('ResourceValidator', function () {
});
it('should not validate map with bad value', function () {
- const map = new Map([['$class', 'org.acme.map@1.0.0.PhoneBook'], ['Lorem', 3]]);
+ const map = new Map([['Lorem', 3]]);
const typedStack = new TypedStack(map);
const mapDeclaration = modelManager.getType('org.acme.map@1.0.0.PhoneBook');
const parameters = { stack : typedStack, 'modelManager' : modelManager, rootResourceIdentifier : 'TEST' };
(() => {
mapDeclaration.accept(resourceValidator,parameters );
- }).should.throw('Model violation in org.acme.map@1.0.0.PhoneBook. Expected Type of String but found \'3\' instead.');
+ }).should.throw('Model violation in the "TEST" instance. The field "PhoneBook_map_value" has a value of "3" (type of value: "number"). Expected type of value: "String".');
});
it('should not validate map with bad key', function () {
- const map = new Map([['$class', 'org.acme.map@1.0.0.PhoneBook'], [1, 'Ipsum']]);
+ const map = new Map([[1, 'Ipsum']]);
const typedStack = new TypedStack(map);
const mapDeclaration = modelManager.getType('org.acme.map@1.0.0.PhoneBook');
const parameters = { stack : typedStack, 'modelManager' : modelManager, rootResourceIdentifier : 'TEST' };
(() => {
mapDeclaration.accept(resourceValidator,parameters );
- }).should.throw('Model violation in org.acme.map@1.0.0.PhoneBook. Expected Type of String but found \'1\' instead');
+ }).should.throw('Model violation in the "TEST" instance. The field "PhoneBook_map_key" has a value of "1" (type of value: "number"). Expected type of value: "String".');
});
});
diff --git a/packages/concerto-core/types/lib/decoratorextractor.d.ts b/packages/concerto-core/types/lib/decoratorextractor.d.ts
deleted file mode 100644
index 432b3b6426..0000000000
--- a/packages/concerto-core/types/lib/decoratorextractor.d.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-export = DecoratorExtractor;
-/**
- * Utility functions to work with
- * [DecoratorCommandSet](https://models.accordproject.org/concerto/decorators.cto)
- * @memberof module:concerto-core
- */
-declare class DecoratorExtractor {
- /**
- * Create the DecoratorExtractor.
- * @constructor
- * @param {boolean} removeDecoratorsFromModel - flag to determine whether to remove decorators from source model
- * @param {string} locale - locale for extracted vocabularies
- * @param {string} dcs_version - version string
- * @param {Object} sourceModelAst - the ast of source models
- */
- constructor(removeDecoratorsFromModel: boolean, locale: string, dcs_version: string, sourceModelAst: any);
- extractionDictionary: {};
- removeDecoratorsFromModel: boolean;
- locale: string;
- dcs_version: string;
- sourceModelAst: any;
- updatedModelAst: any;
- /**
- * Adds a key-value pair to a dictionary (object) if the key exists,
- * or creates a new key with the provided value.
- *
- * @param {string} key - The key to add or update.
- * @param {any} value - The value to add or update.
- * @param {Object} options - options containing target
- * @param {string} options.declaration - Target declaration
- * @param {string} options.property - Target property
- * @param {string} options.mapElement - Target map element
- * @private
- */
- private constructDCSDictionary;
- /**
- * Transforms the collected decorators into proper decorator command sets
- * @param {Array