Skip to content

Commit ad780d0

Browse files
feat(decorator): added new methods to extract vocab or non-vocab decorators from model (#888)
* feat(extractor): added new methods to extract vocab or non-vocab decorators from model and fixed some bugs Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: added description for action Signed-off-by: Dibyam Agrawal <[email protected]> * fix: static variable related issues Signed-off-by: Dibyam Agrawal <[email protected]> * fix: remove decorator issue Signed-off-by: Dibyam Agrawal <[email protected]> * fix: corrected the version number Signed-off-by: Dibyam Agrawal <[email protected]> * fix: updated method from public to private Signed-off-by: Dibyam Agrawal <[email protected]> * fix: updated param name Signed-off-by: Dibyam Agrawal <[email protected]> * fix: made action an optional param Signed-off-by: Dibyam Agrawal <[email protected]> * fix: map decorator issue and updated validateLocale method Signed-off-by: Dibyam Agrawal <[email protected]> * fix: added a null check on model decorator Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: code refactor and updated tests Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: test case changes Signed-off-by: Dibyam Agrawal <[email protected]> * fix: used startwith instead of regex Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: minor code refactor Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: added comment to code for more clearity Signed-off-by: Dibyam Agrawal <[email protected]> * refactor: updated filterdecorators function Signed-off-by: Dibyam Agrawal <[email protected]> --------- Signed-off-by: Dibyam Agrawal <[email protected]>
1 parent e6e0771 commit ad780d0

File tree

10 files changed

+532
-45
lines changed

10 files changed

+532
-45
lines changed

packages/concerto-core/api.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,12 @@ class Concerto {
5555
+ string getNamespace(obj)
5656
}
5757
+ object setCurrentTime()
58-
class DecoratorExtractor {
59-
+ void constructor(boolean,string,string,Object)
60-
}
6158
class DecoratorManager {
6259
+ ModelManager validate(decoratorCommandSet,ModelFile[]) throws Error
6360
+ ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?,boolean?,boolean?)
6461
+ ExtractDecoratorsResult extractDecorators(ModelManager,object,boolean,string)
62+
+ ExtractDecoratorsResult extractVocabularies(ModelManager,object,boolean,string)
63+
+ ExtractDecoratorsResult extractNonVocabDecorators(ModelManager,object,boolean,string)
6564
+ void validateCommand(ModelManager,command)
6665
+ Boolean falsyOrEqual(string||,string[])
6766
+ void applyDecorator(decorated,string,newDecorator)

packages/concerto-core/changelog.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
# Note that the latest public API is documented using JSDocs and is available in api.txt.
2525
#
2626

27+
Version 3.17.3 {1c15de121dd80375cf531bc2244efdd0} 2024-07-26
28+
- Added new methods to extract vocab or non-vocab decorators from model
29+
- Fixed some minor bugs related to vocabulary parsing
30+
- Exposed method to validate locale
31+
2732
Version 3.17.2 {eb3903401fdcf7c26cca1f3f8a029171} 2024-07-17
2833
- Added new optimized methods for decorating models with decorator commandsets
2934
- fixed other decorator command set bugs

packages/concerto-core/lib/decoratorextractor.js

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,45 @@ const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
2222
* Utility functions to work with
2323
* [DecoratorCommandSet](https://models.accordproject.org/concerto/decorators.cto)
2424
* @memberof module:concerto-core
25+
* @private
2526
*/
2627
class DecoratorExtractor {
28+
/**
29+
* The action to be performed to extract all, only vocab or only non-vocab decorators
30+
*/
31+
static Action = {
32+
EXTRACT_ALL: 0,
33+
EXTRACT_VOCAB: 1,
34+
EXTRACT_NON_VOCAB: 2
35+
};
36+
2737
/**
2838
* Create the DecoratorExtractor.
2939
* @constructor
3040
* @param {boolean} removeDecoratorsFromModel - flag to determine whether to remove decorators from source model
3141
* @param {string} locale - locale for extracted vocabularies
3242
* @param {string} dcs_version - version string
3343
* @param {Object} sourceModelAst - the ast of source models
44+
* @param {int} [action=DecoratorExtractor.Action.EXTRACT_ALL] - the action to be performed
3445
*/
35-
constructor(removeDecoratorsFromModel, locale, dcs_version, sourceModelAst) {
46+
constructor(removeDecoratorsFromModel, locale, dcs_version, sourceModelAst, action = DecoratorExtractor.Action.EXTRACT_ALL) {
3647
this.extractionDictionary = {};
3748
this.removeDecoratorsFromModel = removeDecoratorsFromModel;
3849
this.locale = locale;
3950
this.dcs_version = dcs_version;
4051
this.sourceModelAst = sourceModelAst;
4152
this.updatedModelAst = sourceModelAst;
53+
this.action = Object.values(DecoratorExtractor.Action).includes(action)? action : DecoratorExtractor.Action.EXTRACT_ALL;
54+
}
55+
56+
/**
57+
* Returns if the decorator is vocab or not
58+
* @param {string} decoractorName - the name of decorator
59+
* @returns {boolean} - returns true if the decorator is a vocabulary decorator else false
60+
* @private
61+
*/
62+
isVocabDecorator(decoractorName) {
63+
return decoractorName === 'Term' || decoractorName.startsWith('Term_');
4264
}
4365
/**
4466
* Adds a key-value pair to a dictionary (object) if the key exists,
@@ -105,18 +127,24 @@ class DecoratorExtractor {
105127
Object.keys(vocabObject).forEach(decl =>{
106128
if (vocabObject[decl].term){
107129
strVoc += ` - ${decl}: ${vocabObject[decl].term}\n`;
108-
const otherProps = Object.keys(vocabObject[decl]).filter((str)=>str !== 'term' && str !== 'propertyVocabs');
130+
}
131+
const otherProps = Object.keys(vocabObject[decl]).filter((str)=>str !== 'term' && str !== 'propertyVocabs');
132+
//If a declaration does not have any Term decorator, then add Term_ decorators to yaml
133+
if(otherProps.length > 0){
134+
if (!vocabObject[decl].term){
135+
strVoc += ` - ${decl}: ${decl}\n`;
136+
}
109137
otherProps.forEach(key =>{
110138
strVoc += ` ${key}: ${vocabObject[decl][key]}\n`;
111139
});
112140
}
113141
if (vocabObject[decl].propertyVocabs && Object.keys(vocabObject[decl].propertyVocabs).length > 0){
114-
if (!vocabObject[decl].term){
142+
if (!vocabObject[decl].term && otherProps.length === 0){
115143
strVoc += ` - ${decl}: ${decl}\n`;
116144
}
117145
strVoc += ' properties:\n';
118146
Object.keys(vocabObject[decl].propertyVocabs).forEach(prop =>{
119-
strVoc += ` - ${prop}: ${vocabObject[decl].propertyVocabs[prop].term}\n`;
147+
strVoc += ` - ${prop}: ${vocabObject[decl].propertyVocabs[prop].term || ''}\n`;
120148
const otherProps = Object.keys(vocabObject[decl].propertyVocabs[prop]).filter((str)=>str !== 'term');
121149
otherProps.forEach(key =>{
122150
strVoc += ` ${key}: ${vocabObject[decl].propertyVocabs[prop][key]}\n`;
@@ -205,6 +233,18 @@ class DecoratorExtractor {
205233
dictVoc[decl.declaration].propertyVocabs[decl.property][extensionKey] = dcs.arguments[0].value;
206234
}
207235
}
236+
else if (decl.mapElement !== ''){
237+
if (!dictVoc[decl.declaration].propertyVocabs[decl.mapElement]){
238+
dictVoc[decl.declaration].propertyVocabs[decl.mapElement] = {};
239+
}
240+
if (dcs.name === 'Term'){
241+
dictVoc[decl.declaration].propertyVocabs[decl.mapElement].term = dcs.arguments[0].value;
242+
}
243+
else {
244+
const extensionKey = dcs.name.split('Term_')[1];
245+
dictVoc[decl.declaration].propertyVocabs[decl.mapElement][extensionKey] = dcs.arguments[0].value;
246+
}
247+
}
208248
else {
209249
if (dcs.name === 'Term'){
210250
dictVoc[decl.declaration].term = dcs.arguments[0].value;
@@ -227,30 +267,54 @@ class DecoratorExtractor {
227267
let vocabData = [];
228268
Object.keys(this.extractionDictionary).forEach(namespace => {
229269
const jsonData = this.extractionDictionary[namespace];
230-
const patternToDetermineVocab = /^Term_/i;
231270
let dcsObjects = [];
232271
let vocabObject = {};
233272
jsonData.forEach(obj =>{
234273
const decos = JSON.parse(obj.dcs);
235274
const target = this.constructTarget(namespace, obj);
236275
decos.forEach(dcs =>{
237-
if (dcs.name !== 'Term' && !patternToDetermineVocab.test(dcs.name)){
276+
const isVocab = this.isVocabDecorator(dcs.name);
277+
if (!isVocab && this.action !== DecoratorExtractor.Action.EXTRACT_VOCAB){
238278
dcsObjects = this.parseNonVocabularyDecorators(dcsObjects, dcs, this.dcs_version, target);
239279
}
240-
else {
280+
if (isVocab && this.action !== DecoratorExtractor.Action.EXTRACT_NON_VOCAB){
241281
vocabObject = this.parseVocabularies(vocabObject, obj, dcs);
242282
}
243283
});
244284
});
245-
decoratorData = this.transformNonVocabularyDecorators(dcsObjects, namespace, decoratorData);
246-
vocabData = this.transformVocabularyDecorators(vocabObject, namespace, vocabData);
285+
if(this.action !== DecoratorExtractor.Action.EXTRACT_VOCAB){
286+
decoratorData = this.transformNonVocabularyDecorators(dcsObjects, namespace, decoratorData);
287+
}
288+
if(this.action !== DecoratorExtractor.Action.EXTRACT_NON_VOCAB){
289+
vocabData = this.transformVocabularyDecorators(vocabObject, namespace, vocabData);
290+
}
247291
});
248292
return {
249293
decoratorCommandSet: decoratorData,
250294
vocabularies: vocabData
251295
};
252296
}
297+
/**
298+
* Filter vocab or non-vocab decorators
299+
* @param {Object} decorators - the collection of decorators
300+
* @returns {Object} - the collection of filtered decorators
301+
* @private
302+
*/
303+
filterOutDecorators(decorators){
304+
if(!this.removeDecoratorsFromModel){
305+
return decorators;
306+
}
253307

308+
if (this.action === DecoratorExtractor.Action.EXTRACT_ALL){
309+
return undefined;
310+
}
311+
else if(this.action === DecoratorExtractor.Action.EXTRACT_VOCAB){
312+
return decorators.filter((dcs) => !this.isVocabDecorator(dcs.name));
313+
}
314+
else{
315+
return decorators.filter((dcs) => this.isVocabDecorator(dcs.name));
316+
}
317+
}
254318
/**
255319
* Process the map declarations to extract the decorators.
256320
*
@@ -267,9 +331,7 @@ class DecoratorExtractor {
267331
mapElement: 'KEY'
268332
};
269333
this.constructDCSDictionary(namespace, declaration.key.decorators, constructOptions);
270-
if (this.removeDecoratorsFromModel){
271-
declaration.key.decorators = undefined;
272-
}
334+
declaration.key.decorators = this.filterOutDecorators(declaration.key.decorators);
273335
}
274336
}
275337
if (declaration.value){
@@ -279,9 +341,7 @@ class DecoratorExtractor {
279341
mapElement: 'VALUE'
280342
};
281343
this.constructDCSDictionary(namespace, declaration.value.decorators, constructOptions);
282-
if (this.removeDecoratorsFromModel){
283-
declaration.value.decorators = undefined;
284-
}
344+
declaration.value.decorators = this.filterOutDecorators(declaration.value.decorators);
285345
}
286346
}
287347
return declaration;
@@ -304,9 +364,7 @@ class DecoratorExtractor {
304364
property: property.name
305365
};
306366
this.constructDCSDictionary(namespace, property.decorators, constructOptions );
307-
}
308-
if (this.removeDecoratorsFromModel){
309-
property.decorators = undefined;
367+
property.decorators = this.filterOutDecorators(property.decorators);
310368
}
311369
return property;
312370
});
@@ -328,9 +386,7 @@ class DecoratorExtractor {
328386
declaration: decl.name,
329387
};
330388
this.constructDCSDictionary(namespace, decl.decorators, constructOptions);
331-
}
332-
if (this.removeDecoratorsFromModel){
333-
decl.decorators = undefined;
389+
decl.decorators = this.filterOutDecorators(decl.decorators);
334390
}
335391
if (decl.$class === `${MetaModelNamespace}.MapDeclaration`) {
336392
const processedMapDecl = this.processMapDeclaration(decl, namespace);
@@ -352,11 +408,9 @@ class DecoratorExtractor {
352408
*/
353409
processModels(){
354410
const processedModels = this.sourceModelAst.models.map(model =>{
355-
if ((model?.decorators.length > 0)){
411+
if ((model?.decorators?.length > 0)){
356412
this.constructDCSDictionary(model.namespace, model.decorators, {});
357-
if (this.removeDecoratorsFromModel){
358-
model.decorators = undefined;
359-
}
413+
model.decorators = this.filterOutDecorators(model.decorators);
360414
}
361415
const processedDecl = this.processDeclarations(model.declarations, model.namespace);
362416
model.declarations = processedDecl;

packages/concerto-core/lib/decoratormanager.js

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -424,15 +424,11 @@ class DecoratorManager {
424424
return newModelManager;
425425
}
426426
/**
427-
* @typedef decoratorCommandSet
428-
* @type {object}
429-
* @typedef vocabularies
430-
* @type {string}
431427
* @typedef ExtractDecoratorsResult
432428
* @type {object}
433429
* @property {ModelManager} modelManager - A model manager containing models stripped without decorators
434-
* @property {decoratorCommandSet} object[] - Stripped out decorators, formed into decorator command sets
435-
* @property {vocabularies} object[] - Stripped out vocabularies, formed into vocabulary files
430+
* @property {*} decoratorCommandSet - Stripped out decorators, formed into decorator command sets
431+
* @property {string[]} vocabularies - Stripped out vocabularies, formed into vocabulary files
436432
*/
437433
/**
438434
* Extracts all the decorator commands from all the models in modelManager
@@ -449,15 +445,58 @@ class DecoratorManager {
449445
...options
450446
};
451447
const sourceAst = modelManager.getAst(true);
452-
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst);
448+
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst, DecoratorExtractor.Action.EXTRACT_ALL);
453449
const collectionResp = decoratorExtrator.extract();
454450
return {
455451
modelManager: collectionResp.updatedModelManager,
456452
decoratorCommandSet: collectionResp.decoratorCommandSet,
457453
vocabularies: collectionResp.vocabularies
458454
};
459455
}
460-
456+
/**
457+
* Extracts all the vocab decorator commands from all the models in modelManager
458+
* @param {ModelManager} modelManager the input model manager
459+
* @param {object} options - decorator models options
460+
* @param {boolean} options.removeDecoratorsFromModel - flag to strip out vocab decorators from models
461+
* @param {string} options.locale - locale for extracted vocabulary set
462+
* @returns {ExtractDecoratorsResult} - a new model manager with/without the decorators and vocab yamls
463+
*/
464+
static extractVocabularies(modelManager,options) {
465+
options = {
466+
removeDecoratorsFromModel: false,
467+
locale:'en',
468+
...options
469+
};
470+
const sourceAst = modelManager.getAst(true);
471+
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst, DecoratorExtractor.Action.EXTRACT_VOCAB);
472+
const collectionResp = decoratorExtrator.extract();
473+
return {
474+
modelManager: collectionResp.updatedModelManager,
475+
vocabularies: collectionResp.vocabularies
476+
};
477+
}
478+
/**
479+
* Extracts all the non-vocab decorator commands from all the models in modelManager
480+
* @param {ModelManager} modelManager the input model manager
481+
* @param {object} options - decorator models options
482+
* @param {boolean} options.removeDecoratorsFromModel - flag to strip out non-vocab decorators from models
483+
* @param {string} options.locale - locale for extracted vocabulary set
484+
* @returns {ExtractDecoratorsResult} - a new model manager with/without the decorators and a list of extracted decorator jsons
485+
*/
486+
static extractNonVocabDecorators(modelManager,options) {
487+
options = {
488+
removeDecoratorsFromModel: false,
489+
locale:'en',
490+
...options
491+
};
492+
const sourceAst = modelManager.getAst(true);
493+
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst, DecoratorExtractor.Action.EXTRACT_NON_VOCAB);
494+
const collectionResp = decoratorExtrator.extract();
495+
return {
496+
modelManager: collectionResp.updatedModelManager,
497+
decoratorCommandSet: collectionResp.decoratorCommandSet
498+
};
499+
}
461500
/**
462501
* Throws an error if the decoractor command is invalid
463502
* @param {ModelManager} validationModelManager the validation model manager

0 commit comments

Comments
 (0)