From 630d247457cb0ebd3183051b6270c96454daf99e Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:24:53 +0530 Subject: [PATCH 1/7] feat(alias import): Optional field aliasImport added to basemodelmanager - TODO marked - aliasImport handling in runtime separated from rest code to prevent breaking changes Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- packages/concerto-core/lib/basemodelmanager.js | 2 ++ packages/concerto-core/lib/introspect/modelfile.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index 9c2a36f1c..1b38ca430 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -82,6 +82,7 @@ class BaseModelManager { * @param {boolean} [options.metamodelValidation] - When true, modelfiles will be validated * @param {boolean} [options.addMetamodel] - When true, the Concerto metamodel is added to the model manager * @param {boolean} [options.enableMapType] - When true, the Concerto Map Type feature is enabled + * @param {boolean} [options.enableAliasedType] - When true, the Concerto Aliasing feature is enabled * @param {*} [processFile] - how to obtain a concerto AST from an input to the model manager */ constructor(options, processFile) { @@ -97,6 +98,7 @@ class BaseModelManager { // TODO Remove on release of MapType // Supports both env var and property based flag this.enableMapType = !!options?.enableMapType; + this.enableAliasedType = !!options?.enableAliasedType; // Cache a copy of the Metamodel ModelFile for use when validating the structure of ModelFiles later. this.metamodelModelFile = new ModelFile(this, MetaModelUtil.metaModelAst, undefined, MetaModelNamespace); diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index e95d31ad4..e9694518d 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -711,6 +711,7 @@ class ModelFile extends Decorated { * Populate from an AST * @param {object} ast - the AST obtained from the parser * @private + * TODO: Update the code for aliased imports */ fromAst(ast) { const nsInfo = ModelUtil.parseNamespace(ast.namespace); @@ -738,6 +739,7 @@ class ModelFile extends Decorated { ); } + // TODO:handle ImportTypes this.imports = imports; this.imports.forEach((imp) => { this.enforceImportVersioning(imp); From b48a3753377a327274818ed45d60c88cd72a1c6c Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Fri, 21 Jun 2024 01:34:45 +0530 Subject: [PATCH 2/7] feat(alias import): modelfile.js alias imports handled - alias types mapped to fqn in the importShortNames Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- .../concerto-core/lib/basemodelmanager.js | 8 +++++++ .../concerto-core/lib/introspect/modelfile.js | 23 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index 1b38ca430..ec1507c5d 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -124,6 +124,14 @@ class BaseModelManager { return this.strict; } + /** + * Returns the value of the enableAliasedType option + * @returns {boolean} true if the enableAliasedType has been set + */ + isAliasedTypeEnabled() { + return this.enableAliasedType; + } + /** * Adds root types * @private diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index e9694518d..e4bec4077 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -219,7 +219,7 @@ class ModelFile extends Decorated { */ validate() { super.validate(); - + // TODO // A dictionary of imports to versions to track unique namespaces const importsMap = new Map(); @@ -752,9 +752,24 @@ class ModelFile extends Decorated { this.importWildcardNamespaces.push(imp.namespace); break; case `${MetaModelNamespace}.ImportTypes`: - imp.types.forEach( type => { - this.importShortNames.set(type, `${imp.namespace}.${type}`); - }); + if (this.getModelManager().isAliasedTypeEnabled()) { + // map: alias name to the fqn's + // imp.types and imp.aliasedTypes both are available + let aliasedTypes = new Map(); + if (imp.aliasedTypes) { + imp.aliasedTypes.forEach(({ name, aliasName }) => { + aliasedTypes.set(name, aliasName); + }); + } + imp.types.forEach((type)=> aliasedTypes.has(type)? this.importShortNames.set(aliasedTypes[type],`${imp.namespace}.${type}`):this.importShortNames.set(type,`${imp.namespace}.${type}`)); + } else { + if (imp.aliasedTypes) { + throw new Error('Aliasing disabled, set enableAliasType to true'); + } + imp.types.forEach((type) => { + this.importShortNames.set(type,`${imp.namespace}.${type}`); + }); + } break; default: this.importShortNames.set(imp.name, ModelUtil.importFullyQualifiedNames(imp)[0]); From bd3926a0cf258b963f82fe9daad0906eeeaba9c2 Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:22:25 +0530 Subject: [PATCH 3/7] feat(import alias):concerto-core modefile update - Handled PR changes - types can't be aliased to primitive types Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- packages/concerto-core/lib/basemodelmanager.js | 4 ++-- packages/concerto-core/lib/introspect/modelfile.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index ec1507c5d..78507b18d 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -125,8 +125,8 @@ class BaseModelManager { } /** - * Returns the value of the enableAliasedType option - * @returns {boolean} true if the enableAliasedType has been set + * Checks if the import aliasing feature is enabled. + * @returns {boolean} */ isAliasedTypeEnabled() { return this.enableAliasedType; diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index e4bec4077..e1d580ad5 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -711,7 +711,6 @@ class ModelFile extends Decorated { * Populate from an AST * @param {object} ast - the AST obtained from the parser * @private - * TODO: Update the code for aliased imports */ fromAst(ast) { const nsInfo = ModelUtil.parseNamespace(ast.namespace); @@ -755,9 +754,12 @@ class ModelFile extends Decorated { if (this.getModelManager().isAliasedTypeEnabled()) { // map: alias name to the fqn's // imp.types and imp.aliasedTypes both are available - let aliasedTypes = new Map(); + const aliasedTypes = new Map(); if (imp.aliasedTypes) { imp.aliasedTypes.forEach(({ name, aliasName }) => { + if(ModelUtil.isPrimitiveType(aliasName)){ + throw new Error('Types cannot be aliased to primitive type'); + } aliasedTypes.set(name, aliasName); }); } From bb08d49a5cc1c45c1fe27ecf7ec5c39e9676ee95 Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:41:00 +0530 Subject: [PATCH 4/7] feat(alias import): Test Cases added Following are the test-cases added: - resolve alias type from import - validate alias type - validate map using alias type as value Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- .../concerto-core/lib/introspect/modelfile.js | 2 +- .../test/introspect/modelfile.js | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index e1d580ad5..561f42f24 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -763,7 +763,7 @@ class ModelFile extends Decorated { aliasedTypes.set(name, aliasName); }); } - imp.types.forEach((type)=> aliasedTypes.has(type)? this.importShortNames.set(aliasedTypes[type],`${imp.namespace}.${type}`):this.importShortNames.set(type,`${imp.namespace}.${type}`)); + imp.types.forEach((type)=> aliasedTypes.has(type)? this.importShortNames.set(aliasedTypes.get(type),`${imp.namespace}.${type}`):this.importShortNames.set(type,`${imp.namespace}.${type}`)); } else { if (imp.aliasedTypes) { throw new Error('Aliasing disabled, set enableAliasType to true'); diff --git a/packages/concerto-core/test/introspect/modelfile.js b/packages/concerto-core/test/introspect/modelfile.js index 16718d3d7..598997da0 100644 --- a/packages/concerto-core/test/introspect/modelfile.js +++ b/packages/concerto-core/test/introspect/modelfile.js @@ -608,6 +608,92 @@ describe('ModelFile', () => { }); + describe('#aliasedImport', () => { + + beforeEach(()=>{ + modelManager.enableAliasedType=true; + }); + it('should resolve aliased name of import type', () => { + const model = ` + namespace org.acme + import org.saluja.{doc as d}`; + + let modelFile = ParserUtil.newModelFile(modelManager, model); + modelFile.resolveImport('d').should.equal('org.saluja.doc'); + }); + + it('should not throw if an aliased import exists for a type that exists in a valid namespace', () => { + const model1 = ` + namespace org.saluja.ext + asset MyAsset2 identified by assetId { + o String assetId + }`; + const model2 = ` + namespace org.acme + import org.saluja.ext.{MyAsset2 as m} + asset MyAsset identified by assetId { + o String assetId + o m[] arr + }`; + let modelFile1 = ParserUtil.newModelFile(modelManager, model1); + modelManager.addModelFile(modelFile1); + let modelFile2 = ParserUtil.newModelFile(modelManager, model2); + (() => modelFile2.validate()).should.not.throw(); + }); + + it('should not throw if an duplicate types aliased to distinct aliased names', () => { + const model1 = ` + namespace org.saluja + asset MyAsset identified by assetId { + o String assetId + }`; + const model2 = ` + namespace org.acme + asset MyAsset identified by assetId { + o String assetId + }`; + + const model3 = ` + namespace org.acme2 + import org.saluja.{MyAsset as m1} + import org.acme.{MyAsset as m2} + + asset MyAsset identified by assetId { + o String assetId + o m1[] arr1 + o m2[] arr2 + }`; + let modelFile1 = ParserUtil.newModelFile(modelManager, model1); + modelManager.addModelFile(modelFile1); + let modelFile2 = ParserUtil.newModelFile(modelManager, model2); + modelManager.addModelFile(modelFile2); + let modelFile3 = ParserUtil.newModelFile(modelManager, model3); + (() => modelFile3.validate()).should.not.throw(); + }); + + it('should not throw if map value is an aliased type', () => { + const model1 = ` + namespace org.saluja + asset Student identified by rollno { + o String rollno + }`; + const model2 = ` + namespace org.acme + import org.saluja.{Student as stud} + + map StudMap{ + o DateTime + o stud + }`; + modelManager.enableMapType=true; + let modelFile1 = ParserUtil.newModelFile(modelManager, model1); + modelManager.addModelFile(modelFile1); + let modelFile2 = ParserUtil.newModelFile(modelManager, model2); + (() => modelFile2.validate()).should.not.throw(); + }); + }); + + describe('#isDefined', () => { let modelManager; From 68b37ac54a0fea48716f2a27ca98b522bbd593ef Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:27:04 +0530 Subject: [PATCH 5/7] feat(alias import): Types updated ; Minor version updated Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- packages/concerto-core/api.txt | 3 ++- packages/concerto-core/changelog.txt | 3 +++ packages/concerto-core/lib/basemodelmanager.js | 2 +- packages/concerto-core/package.json | 2 +- packages/concerto-core/types/lib/basemodelmanager.d.ts | 9 +++++++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 139c8c287..1aa074c41 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -2,9 +2,10 @@ class AstModelManager extends BaseModelManager { + void constructor(object?) } class BaseModelManager { - + void constructor(object?,boolean?,Object?,boolean?,boolean?,boolean?,processFile?) + + void constructor(object?,boolean?,Object?,boolean?,boolean?,boolean?,boolean?,processFile?) + boolean isModelManager() + boolean isStrict() + + boolean isAliasedTypeEnabled() + Object accept(Object,Object) + void validateModelFile(string|ModelFile,string?) throws IllegalModelException + Object addModelFile(ModelFile,string?,string?,boolean?) throws IllegalModelException diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index d5e1b93ff..b9aa7cd58 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,6 +24,9 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # +Version 3.17.1 {fc7abec4765b5e1672dab7c6032dfdc3} 2024-06-21 +- Added 'enableAliasedType' option to BaseModelManager +- Aliased types mapped to FQN in modelfile Version 3.16.7 {8f455df1e788c4994f423d6e236bee21} 2024-05-01 - Added missing `strictQualifiedDateTimes` option to Serializer.fromJSON diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index 78507b18d..0052a94d8 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -126,7 +126,7 @@ class BaseModelManager { /** * Checks if the import aliasing feature is enabled. - * @returns {boolean} + * @returns {boolean} true if the enableAliasedType has been set */ isAliasedTypeEnabled() { return this.enableAliasedType; diff --git a/packages/concerto-core/package.json b/packages/concerto-core/package.json index 932fe7abf..ccddce401 100644 --- a/packages/concerto-core/package.json +++ b/packages/concerto-core/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/concerto-core", - "version": "3.17.0", + "version": "3.17.1", "description": "Core Implementation for the Concerto Modeling Language", "homepage": "https://github.com/accordproject/concerto", "engines": { diff --git a/packages/concerto-core/types/lib/basemodelmanager.d.ts b/packages/concerto-core/types/lib/basemodelmanager.d.ts index ccf0fdd4e..cf7318ded 100644 --- a/packages/concerto-core/types/lib/basemodelmanager.d.ts +++ b/packages/concerto-core/types/lib/basemodelmanager.d.ts @@ -24,6 +24,7 @@ declare class BaseModelManager { * @param {boolean} [options.metamodelValidation] - When true, modelfiles will be validated * @param {boolean} [options.addMetamodel] - When true, the Concerto metamodel is added to the model manager * @param {boolean} [options.enableMapType] - When true, the Concerto Map Type feature is enabled + * @param {boolean} [options.enableAliasedType] - When true, the Concerto Aliasing feature is enabled * @param {*} [processFile] - how to obtain a concerto AST from an input to the model manager */ constructor(options?: { @@ -32,6 +33,7 @@ declare class BaseModelManager { metamodelValidation?: boolean; addMetamodel?: boolean; enableMapType?: boolean; + enableAliasedType?: boolean; }, processFile?: any); processFile: any; modelFiles: {}; @@ -45,8 +47,10 @@ declare class BaseModelManager { metamodelValidation?: boolean; addMetamodel?: boolean; enableMapType?: boolean; + enableAliasedType?: boolean; }; enableMapType: boolean; + enableAliasedType: boolean; metamodelModelFile: any; /** * Returns true @@ -58,6 +62,11 @@ declare class BaseModelManager { * @returns {boolean} true if the strict has been set */ isStrict(): boolean; + /** + * Returns the value of the enableAliasedType option + * @returns {boolean} true if the enableAliasedType has been set + */ + isAliasedTypeEnabled(): boolean; /** * Adds root types * @private From 56356cae1a2b55aabf876ac8f7f42c968d42c95a Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:23:31 +0530 Subject: [PATCH 6/7] feat(alias): Pr suggestion added - Added new test case where a concept is extended on a aliased import type concept - removed unecessary comments and formated code Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- .../concerto-core/lib/introspect/modelfile.js | 17 ++++++++++---- .../test/introspect/modelfile.js | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index 561f42f24..a879331c9 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -219,7 +219,6 @@ class ModelFile extends Decorated { */ validate() { super.validate(); - // TODO // A dictionary of imports to versions to track unique namespaces const importsMap = new Map(); @@ -738,7 +737,6 @@ class ModelFile extends Decorated { ); } - // TODO:handle ImportTypes this.imports = imports; this.imports.forEach((imp) => { this.enforceImportVersioning(imp); @@ -752,8 +750,6 @@ class ModelFile extends Decorated { break; case `${MetaModelNamespace}.ImportTypes`: if (this.getModelManager().isAliasedTypeEnabled()) { - // map: alias name to the fqn's - // imp.types and imp.aliasedTypes both are available const aliasedTypes = new Map(); if (imp.aliasedTypes) { imp.aliasedTypes.forEach(({ name, aliasName }) => { @@ -763,7 +759,18 @@ class ModelFile extends Decorated { aliasedTypes.set(name, aliasName); }); } - imp.types.forEach((type)=> aliasedTypes.has(type)? this.importShortNames.set(aliasedTypes.get(type),`${imp.namespace}.${type}`):this.importShortNames.set(type,`${imp.namespace}.${type}`)); + // Local-name(aliased or non-aliased) is mapped to the Fully qualified type name + imp.types.forEach((type) => + aliasedTypes.has(type) + ? this.importShortNames.set( + aliasedTypes.get(type), + `${imp.namespace}.${type}` + ) + : this.importShortNames.set( + type, + `${imp.namespace}.${type}` + ) + ); } else { if (imp.aliasedTypes) { throw new Error('Aliasing disabled, set enableAliasType to true'); diff --git a/packages/concerto-core/test/introspect/modelfile.js b/packages/concerto-core/test/introspect/modelfile.js index 598997da0..8c6208cb7 100644 --- a/packages/concerto-core/test/introspect/modelfile.js +++ b/packages/concerto-core/test/introspect/modelfile.js @@ -691,6 +691,29 @@ describe('ModelFile', () => { let modelFile2 = ParserUtil.newModelFile(modelManager, model2); (() => modelFile2.validate()).should.not.throw(); }); + + it('should not throw if declaration is extended on a aliased type declaration', () => { + const model1 = ` + namespace org.saluja + + scalar nickname extends String + asset Vehicle identified by serialno { + o String serialno + }`; + const model2 = ` + namespace org.acme + import org.saluja.{Vehicle as V,nickname as nk} + + asset Car extends V{ + o String company + o nk shortname + }`; + modelManager.enableMapType = true; + let modelFile1 = ParserUtil.newModelFile(modelManager, model1); + modelManager.addModelFile(modelFile1); + let modelFile2 = ParserUtil.newModelFile(modelManager, model2); + (() => modelFile2.validate()).should.not.throw(); + }); }); From e84cf14d4ee8410edac8aa697ef4ba57bd835926 Mon Sep 17 00:00:00 2001 From: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:02:15 +0530 Subject: [PATCH 7/7] feat(alias): aliasName renamed to aliasedName Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com> --- packages/concerto-core/lib/introspect/modelfile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index a879331c9..e269c35f4 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -752,11 +752,11 @@ class ModelFile extends Decorated { if (this.getModelManager().isAliasedTypeEnabled()) { const aliasedTypes = new Map(); if (imp.aliasedTypes) { - imp.aliasedTypes.forEach(({ name, aliasName }) => { - if(ModelUtil.isPrimitiveType(aliasName)){ + imp.aliasedTypes.forEach(({ name, aliasedName }) => { + if(ModelUtil.isPrimitiveType(aliasedName)){ throw new Error('Types cannot be aliased to primitive type'); } - aliasedTypes.set(name, aliasName); + aliasedTypes.set(name, aliasedName); }); } // Local-name(aliased or non-aliased) is mapped to the Fully qualified type name