Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(map): Extend Type support #677

Merged
merged 41 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2029833
feat(map): add to introspection API
jonathan-casey Aug 1, 2023
1539a64
feat(map): extend JsonGenerator for Map Types
jonathan-casey Aug 1, 2023
fcd0b20
feat(map): extend JsonPopulator for Map Types
jonathan-casey Aug 1, 2023
a3f247a
feat(map): extend ResourceValidator for Map Types
jonathan-casey Aug 1, 2023
4817266
feat(map): add test cases for Map serialisation <> deserialisation
jonathan-casey Aug 1, 2023
d0802b1
feat(map): cleanup
jonathan-casey Aug 1, 2023
916069d
feat(map): add type definitions
jonathan-casey Aug 1, 2023
6c23986
feat(map): satisfy version checker
jonathan-casey Aug 1, 2023
fb3c393
feat(map): remove variable assignment for chai import
jonathan-casey Aug 1, 2023
a788e44
feat(map): satisfy version checker
jonathan-casey Aug 1, 2023
34fbf74
feat(map): update test cases to match on error handling changes
jonathan-casey Aug 1, 2023
116b180
feat(map): cleanup
jonathan-casey Aug 1, 2023
4478693
feat(map): cleanup test cases
jonathan-casey Aug 1, 2023
ffe486a
test(map): add tests for missed cases
mttrbrts Aug 2, 2023
389449e
feat(map): fixes metamodel parsing for MapDeclarations
jonathan-casey Aug 3, 2023
bfbe382
feat(map): printer update reads new metamodel map shape
jonathan-casey Aug 3, 2023
9570985
feat(map): updates test data for new metamodel design
jonathan-casey Aug 3, 2023
57408b5
feat(map): initial rework of introspection
jonathan-casey Aug 4, 2023
42668f9
feat(map): add more test coverage
jonathan-casey Aug 9, 2023
5f627f8
feat(map): improve error messaging & cleanup
jonathan-casey Aug 9, 2023
b5d6c83
feat(map): adds more test coverage for MapKeyType introspection
jonathan-casey Aug 9, 2023
882bf5a
feat(map): adds more test coverage for MapValueType introspection
jonathan-casey Aug 9, 2023
85f8f10
feat(map): refactor serialization
jonathan-casey Aug 10, 2023
2f8d622
feat(map): refactor serialization test coverage
jonathan-casey Aug 10, 2023
427658f
feat(map): cleanup
jonathan-casey Aug 10, 2023
539e393
feat(map): DRY up code in the JSONPopulator
jonathan-casey Aug 10, 2023
bdeeb02
feat(map): adds coverage to new introspection functions
jonathan-casey Aug 10, 2023
b01e444
feat(map): adds type definitions
jonathan-casey Aug 10, 2023
9bce014
feat(map): updates changelog
jonathan-casey Aug 10, 2023
56204f6
fix: Private identifiers are only available when targeting ECMAScript…
jonathan-casey Aug 10, 2023
0d495d5
fix: updates changelog
jonathan-casey Aug 10, 2023
f72a3d0
feat(map): moves isValidMapKey & isValidMapValue to ModelUtils
jonathan-casey Aug 14, 2023
32951fc
feat(map): removes unreachable default case
jonathan-casey Aug 14, 2023
a4d3749
feat(map): removes unreachable code
jonathan-casey Aug 14, 2023
af42e11
feat(map): adds more test coverage
jonathan-casey Aug 14, 2023
926f91d
feat(map): remove dead code
jonathan-casey Aug 14, 2023
04b0798
feat(map): adds JSDoc @private
jonathan-casey Aug 14, 2023
d446e1d
feat(map): adds type defs & change log
jonathan-casey Aug 14, 2023
19dfcf7
feat(map): improve error message
jonathan-casey Aug 16, 2023
74cb997
feat(map): add util method
jonathan-casey Aug 16, 2023
08ce775
fix: update type def
jonathan-casey Aug 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ class MapDeclaration extends Declaration {
+ string getName()
+ string getKey()
+ string getValue()
+ array getProperties()
+ String toString()
+ string declarationKind()
+ boolean isMapDeclaration()
Expand All @@ -174,6 +173,8 @@ class MapKeyType extends Decorated {
+ MapDeclaration getParent()
+ string getType()
+ String toString()
+ boolean isKey()
+ boolean isValue()
}
class MapValueType extends Decorated {
+ void constructor(MapDeclaration,Object) throws IllegalModelException
Expand All @@ -182,6 +183,8 @@ class MapValueType extends Decorated {
+ MapDeclaration getParent()
+ string getType()
+ String toString()
+ boolean isKey()
+ boolean isValue()
}
+ ModelManager newMetaModelManager()
+ object validateMetaModel()
Expand Down
15 changes: 15 additions & 0 deletions packages/concerto-core/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#

Version 3.8.5 {9cd54e67c4a39a3041706e4b37dce9f1} 2023-08-14
- Makes MapKeyType MapValueType processType() private

Version 3.8.4 {a99786670d2561bf2b84cb3eb1bb1a08} 2023-08-14
- Move isValidMapKey & isValidMapValue to ModelUtils

Version 3.8.3 {7dd828f29c49cedfb8eee2f3c6fefa6e} 2023-08-10
- Removes private identifiers from MapKeyType & MapValueType

Version 3.8.2 {1a497711c4cca84d1dfa92c0b581fadf} 2023-08-10
- Add isValidMapKey(Object) isValidMapValue(Object)

Version 3.8.1 {794268f69b81f05f711d38a9ef1a7833} 2023-08-1
- Add To MapKeyType, MapValue functionality

Version 3.7.0 {a97cb6ebd45679354ba4da1940d2bb8d} 2023-05-19
- Add MapDeclaration, MapKeyType, AggregateValueType

Expand Down
30 changes: 8 additions & 22 deletions packages/concerto-core/lib/introspect/mapdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

'use strict';

const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');

const Declaration = require('./declaration');
const IllegalModelException = require('./illegalmodelexception');
const MapValueType = require('./mapvaluetype');
Expand Down Expand Up @@ -65,24 +63,21 @@ class MapDeclaration extends Declaration {
process() {
super.process();

if (this.ast.properties.length !== 2) {
throw new IllegalModelException(`MapDeclaration must contain exactly two properties - MapKeyType & MapyValueType ${this.ast.name}`, this.modelFile, this.ast.location);
if (!this.ast.key || !this.ast.value) {
throw new IllegalModelException(`MapDeclaration must contain Key & Value properties ${this.ast.name}`, this.modelFile, this.ast.location);
}

const key = this.ast.properties.find(property => property.$class === `${MetaModelNamespace}.MapKeyType`);
const value = this.ast.properties.find(property => property.$class === `${MetaModelNamespace}.AggregateValueType` || property.$class === `${MetaModelNamespace}.AggregateRelationshipValueType`);

if (!key) {
throw new IllegalModelException(`MapDeclaration must contain MapKeyType ${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 (!value) {
throw new IllegalModelException(`MapDeclaration must contain AggregateValueType ${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, key);
this.value = new MapValueType(this, value);
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);
}

Expand Down Expand Up @@ -146,15 +141,6 @@ class MapDeclaration extends Declaration {
return this.value;
}

/**
* Returns the MapDeclaration properties
*
* @return {array} the MapDeclaration properties
*/
getProperties() {
return this.ast.properties;
}

/**
* Returns the string representation of this class
* @return {String} the string representation of the class
Expand Down
75 changes: 54 additions & 21 deletions packages/concerto-core/lib/introspect/mapkeytype.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

'use strict';

const ModelUtil = require('../modelutil');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');

const Decorated = require('./decorated');
Expand Down Expand Up @@ -46,7 +47,6 @@ class MapKeyType extends Decorated {
constructor(parent, ast) {
super(ast);
this.parent = parent;
this.type = this.ast.name;
this.process();
}

Expand All @@ -58,6 +58,7 @@ class MapKeyType extends Decorated {
*/
process() {
super.process();
this.processType(this.ast);
}

/**
Expand All @@ -67,30 +68,44 @@ class MapKeyType extends Decorated {
* @protected
*/
validate() {
const declaration = this.getModelFile().getAllDeclarations();
const key = declaration.find(decl => decl.name === this.type);

if (!key?.isConcept?.() &&
!key?.isEnum?.() &&
!key?.isScalarDeclaration?.() &&
!['String', 'DateTime'].includes(this.type)) {
throw new IllegalModelException(`MapKeyType has invalid Type: ${this.type}`);
}

if (key?.isConcept?.() && !key.isIdentified()) {
throw new IllegalModelException(
`ConceptDeclaration must be identified in context of MapKeyType: ${this.type}`
);
}
if (!ModelUtil.isPrimitiveType(this.type)) {
let decl = this.parent.getModelFile().getAllDeclarations().find(d => d.name === 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 (key?.isScalarDeclaration?.() &&
!(key?.ast.$class === `${MetaModelNamespace}.StringScalar`) &&
!(key?.ast.$class === `${MetaModelNamespace}.DateTimeScalar` )) {
throw new IllegalModelException(
`Scalar must be one of StringScalar, DateTimeScalar in context of MapKeyType. Invalid Scalar: ${this.type}`
);
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`
);
}
}
}

/**
* 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`:
decl = this.parent.getModelFile().getAllDeclarations().find(d => d.name === this.ast.type.name);
this.type = decl.getName();
break;
}
}

/**
Expand Down Expand Up @@ -129,6 +144,24 @@ class MapKeyType extends Decorated {
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;
}
}

module.exports = MapKeyType;
99 changes: 81 additions & 18 deletions packages/concerto-core/lib/introspect/mapvaluetype.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
'use strict';

const Decorated = require('./decorated');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const IllegalModelException = require('../../lib/introspect/illegalmodelexception');
const ModelUtil = require('../modelutil');


// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
Expand Down Expand Up @@ -43,7 +46,6 @@ class MapValueType extends Decorated {
constructor(parent, ast) {
super(ast);
this.parent = parent;
this.type = this.ast.name;
this.process();
}

Expand All @@ -55,6 +57,7 @@ class MapValueType extends Decorated {
*/
process() {
super.process();
this.processType(this.ast);
}

/**
Expand All @@ -64,23 +67,65 @@ class MapValueType extends Decorated {
* @protected
*/
validate() {
const declarations = this.getModelFile().getAllDeclarations();

const value = declarations.find(decl => decl.name === this.type);

if (!value?.isConcept?.() &&
!value?.isEnum?.() &&
!value?.isAsset?.() &&
!value?.isEvent?.() &&
!value?.isParticipant?.() &&
!value?.isTransaction?.() &&
!value?.isMapDeclaration?.() &&
!value?.isScalarDeclaration?.() &&
!['String', 'Long', 'Integer', 'Double', 'Boolean', 'DateTime'].includes(this.type)) {

throw new IllegalModelException(
`MapPropertyType has invalid Type: ${this.type}`
);
if (!ModelUtil.isPrimitiveType(this.type)) {
let decl = this.parent.getModelFile().getAllDeclarations().find(d => d.name === 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`:
decl = this.parent.getModelFile().getAllDeclarations().find(d => d.name === this.ast.type.name);

// 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 !== '[email protected]') {
throw new IllegalModelException(`ObjectMapValueType type $class must be of TypeIdentifier for MapDeclaration named ${this.parent.name}`);
}

this.type = decl.getName();
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;
}
}

Expand Down Expand Up @@ -121,6 +166,24 @@ class MapValueType extends Decorated {
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;
}

}

module.exports = MapValueType;
Loading