Skip to content

Commit

Permalink
feat(alias-import): update runtime classes to understand import alias…
Browse files Browse the repository at this point in the history
…ing (#860)

* 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 <[email protected]>

* feat(alias import): modelfile.js alias imports handled

- alias types mapped to fqn in the importShortNames

Signed-off-by: Jaskeerat Singh Saluja <[email protected]>

* feat(import alias):concerto-core modefile update

- Handled PR changes
- types can't be aliased to primitive types

Signed-off-by: Jaskeerat Singh Saluja <[email protected]>

* 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 <[email protected]>

* feat(alias import): Types updated ; Minor version updated

Signed-off-by: Jaskeerat Singh Saluja <[email protected]>

* 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 <[email protected]>

* feat(alias): aliasName renamed to aliasedName

Signed-off-by: Jaskeerat Singh Saluja <[email protected]>

---------

Signed-off-by: Jaskeerat Singh Saluja <[email protected]>
  • Loading branch information
salujajaskeerat authored Jul 11, 2024
1 parent 08eeee5 commit 913b95d
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 6 deletions.
3 changes: 2 additions & 1 deletion packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/concerto-core/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.8 {a23d37a4a92071314ff821f879943364} 2024-07-09
- Added a new pathway for applying dcs target at namespace
Expand Down
10 changes: 10 additions & 0 deletions packages/concerto-core/lib/basemodelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -122,6 +124,14 @@ class BaseModelManager {
return this.strict;
}

/**
* Checks if the import aliasing feature is enabled.
* @returns {boolean} true if the enableAliasedType has been set
*/
isAliasedTypeEnabled() {
return this.enableAliasedType;
}

/**
* Adds root types
* @private
Expand Down
34 changes: 30 additions & 4 deletions packages/concerto-core/lib/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ class ModelFile extends Decorated {
*/
validate() {
super.validate();

// A dictionary of imports to versions to track unique namespaces
const importsMap = new Map();

Expand Down Expand Up @@ -756,9 +755,36 @@ 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()) {
const aliasedTypes = new Map();
if (imp.aliasedTypes) {
imp.aliasedTypes.forEach(({ name, aliasedName }) => {
if(ModelUtil.isPrimitiveType(aliasedName)){
throw new Error('Types cannot be aliased to primitive type');
}
aliasedTypes.set(name, aliasedName);
});
}
// 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');
}
imp.types.forEach((type) => {
this.importShortNames.set(type,`${imp.namespace}.${type}`);
});
}
break;
default:
this.importShortNames.set(imp.name, ModelUtil.importFullyQualifiedNames(imp)[0]);
Expand Down
2 changes: 1 addition & 1 deletion packages/concerto-core/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
109 changes: 109 additions & 0 deletions packages/concerto-core/test/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,115 @@ 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();
});

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();
});
});


describe('#isDefined', () => {

let modelManager;
Expand Down
9 changes: 9 additions & 0 deletions packages/concerto-core/types/lib/basemodelmanager.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?: {
Expand All @@ -32,6 +33,7 @@ declare class BaseModelManager {
metamodelValidation?: boolean;
addMetamodel?: boolean;
enableMapType?: boolean;
enableAliasedType?: boolean;
}, processFile?: any);
processFile: any;
modelFiles: {};
Expand All @@ -45,8 +47,10 @@ declare class BaseModelManager {
metamodelValidation?: boolean;
addMetamodel?: boolean;
enableMapType?: boolean;
enableAliasedType?: boolean;
};
enableMapType: boolean;
enableAliasedType: boolean;
metamodelModelFile: any;
/**
* Returns true
Expand All @@ -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
Expand Down

0 comments on commit 913b95d

Please sign in to comment.