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(core):added extract decorator method #754

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bb0ef5e
feat(core):added extract decorator method
Nov 6, 2023
809c932
feat(core):added extract decorator method
Nov 6, 2023
34be7eb
chore:Merged with main
Nov 6, 2023
a512353
fix: Added bugfix for no decorators
Nov 6, 2023
249fbf6
fix: Removed mutability and typos fixed
Nov 7, 2023
deb7331
fix: typo fix for jsdoc
Nov 7, 2023
8863f95
fix: call Model functions for getting version
Nov 7, 2023
c5438f3
feat: Extract decorators and vocab via visitor pattern
Nov 8, 2023
5ff26dd
feat: Extract decorators and vocab via visitor pattern
Nov 8, 2023
f0afdfd
feat: Extract decorators and vocab via visitor pattern
Nov 8, 2023
c85329c
Merge branch 'main' of https://github.com/muskanbararia/concerto into…
Nov 9, 2023
ae03cba
feat: Extract decorators and vocab for all types including maps and a…
Nov 9, 2023
55a2547
fix:minor linting fixes
Nov 9, 2023
a53454d
feat: Added new testcase and created a Util for decorators
Nov 10, 2023
398ba83
feat: Added new testcase and created a Util for decorators
Nov 10, 2023
29e390e
fix: broke down functions so they are more readable
Nov 10, 2023
5d7d4ef
fix: added relevant types
Nov 10, 2023
b0a286d
fix: removed two loops in parsing and better breakdown
Nov 13, 2023
26da05a
fix: better breakdown by adding transform functions
Nov 13, 2023
bb83859
fix: better breakdown by adding constructor functions
Nov 13, 2023
578cbfa
fix: created decorator extractor class
Nov 14, 2023
4cfefe8
fix: created decorator extractor class
Nov 14, 2023
079f3a5
chore: Merge branch 'main' of https://github.com/muskanbararia/concer…
Nov 14, 2023
d86d203
chore: added linting fix
Nov 14, 2023
b030c98
fix: added proper check to test for map type
Nov 14, 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
1 change: 1 addition & 0 deletions packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Concerto {
class DecoratorManager {
+ ModelManager validate(decoratorCommandSet,ModelFile[]) throws Error
+ ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?,boolean?)
+ Object extractDecorators(ModelManager,object,boolean,string)
+ void validateCommand(ModelManager,command)
+ Boolean falsyOrEqual(string||,string[])
+ void applyDecorator(decorated,string,newDecorator)
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.14.2 {86711a7ba556ad7f2be251f03e99b448} 2023-11-07
- Added DCS and vocabulary extraction support for decoratorManager

Version 3.13.1 {f5a9a1ea6a64865843a3abb77798cbb0} 2023-10-18
- Add migrate option to DecoratorManager options

Expand Down
251 changes: 248 additions & 3 deletions packages/concerto-core/lib/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class DecoratorManager {
metamodelValidation: true,
addMetamodel: true,
});
if(modelFiles) {
if (modelFiles) {
validationModelManager.addModelFiles(modelFiles);
}
validationModelManager.addCTOModel(
Expand Down Expand Up @@ -253,6 +253,251 @@ class DecoratorManager {
return newModelManager;
}

/**
* Extracts all the decorator commands from all the models in modelManager
* @param {ModelManager} modelManager the input model manager
* @param {object} options - decorator models options
* @param {boolean} options.removeDecoratorsFromModel - flag to strip out decorators from models
* @param {string} options.locale - locale for extracted vocabulary set
* @returns {Object} a new model manager with the decorations removed and a list of extracted decorator jsons
*/
static extractDecorators(modelManager,options) {
options = {
removeDecoratorsFromModel: false,
locale:'en',
...options
};
const decoratedAst = modelManager.getAst(true);
let extractionDictionary = {};
const processedModels = decoratedAst.models.map((model)=>{
if ((model?.decorators.length > 0)){
extractionDictionary = this.constructDCSDictionary(extractionDictionary,model.namespace,model.decorators,'','');
if (options.removeDecoratorsFromModel){
model.decorators = undefined;
}
}
const processedDecl = model.declarations.map(decl => {
if (decl.decorators) {
extractionDictionary = this.constructDCSDictionary(extractionDictionary,model.namespace,decl.decorators,decl.name,'');
}
if (options.removeDecoratorsFromModel){
decl.decorators = undefined;
}
if (decl.$class.endsWith('.MapDeclaration')) {
if (decl.key){
if (decl.key.decorators){
extractionDictionary = this.constructDCSDictionary(extractionDictionary,model.namespace,decl.key.decorators,decl.name,'','KEY');
decl.key.decorators = undefined;
}
}
if (decl.value){
if (decl.value.decorators){
extractionDictionary = this.constructDCSDictionary(extractionDictionary,model.namespace,decl.value.decorators,decl.name,'','VALUE');
decl.value.decorators = undefined;
}
}
}
if (decl.properties) {
const processedProperties = decl.properties.map((property) => {
if (property.decorators){
extractionDictionary = this.constructDCSDictionary(extractionDictionary,model.namespace, property.decorators,decl.name,property.name);
}
if (options.removeDecoratorsFromModel){
property.decorators = undefined;
}
return property;
});
decl.properties = processedProperties;
}
return decl;
});
model.declarations = processedDecl;
return model;
});
const processedAST={
...decoratedAst,
models:processedModels,
};
const newModelManager = new ModelManager();
newModelManager.fromAst(processedAST);
const decoratorCommandSet = this.parseNonVocabularyDecorators(extractionDictionary);
const vocabularies = this.parseVocabularies(extractionDictionary,options.locale);
return {
modelManager:newModelManager,
decoratorCommandSet,
vocabularies
};
}
/**
* Parses the dict data into an array of decorator jsons
* @param {Object} decoratorDict the input dict
* @returns {Array<Object>} the parsed decorator command set array
* @private
*/
static parseNonVocabularyDecorators(decoratorDict){
const data = [];
Object.keys(decoratorDict).forEach((namespace)=>{
const {name, version} = ModelUtil.parseNamespace(namespace);
const nameOfDcs = name;
const versionOfDcs = version;
const dcsObjects = [];
const jsonData = decoratorDict[namespace];
const patternToDetermineVocab = /^Term_/i;
jsonData.forEach((obj)=>{
const decos = JSON.parse(obj.dcs);
const target = {
'$class': `org.accordproject.decoratorcommands@${DCS_VERSION}.CommandTarget`,
'namespace':namespace
};
if (obj.declaration && obj.declaration!==''){
target.declaration = obj.declaration;
}
if (obj.property && obj.property!==''){
target.property = obj.property;
}
if (obj.mapElement && obj.mapElement!==''){
target.mapElement = obj.mapElement;
}
decos.forEach((dcs)=>{
if (dcs.name !== 'Term' && !patternToDetermineVocab.test(dcs.name)){
const decotatorObj = {
'$class': '[email protected]',
'name': dcs.name,
};
if (dcs.arguments){
const args = dcs.arguments.map((arg)=>{
return {
'$class':arg.$class,
'value':arg.value
};
});
decotatorObj.arguments = args;
}
let dcsObject = {
'$class': `org.accordproject.decoratorcommands@${DCS_VERSION}.Command`,
'type': 'UPSERT',
'target': target,
'decorator': decotatorObj,
};
dcsObjects.push(dcsObject);
}
});
});
const dcmsForNamespace = {
'$class': `org.accordproject.decoratorcommands@${DCS_VERSION}.DecoratorCommandSet`,
'name': nameOfDcs,
'version': versionOfDcs,
'commands': dcsObjects
};
data.push(dcmsForNamespace);
});
return data;
}
/**
* Parses the dict data into an array of decorator jsons
* @param {Object} decoratorDict the input dict
* @param {String} locale locale for target vocab
* @returns {Array<Object>} the parsed decorator command set array
* @private
*/
static parseVocabularies(decoratorDict,locale){
const data = [];
const patternToDetermineVocab = /^Term_/i;
Object.keys(decoratorDict).forEach((namespace)=>{
let strVoc = '';
strVoc = strVoc+`locale: ${locale}\n`;
strVoc = strVoc+`namespace: ${namespace}\n`;
strVoc = strVoc+'declarations:\n';
const jsonData = decoratorDict[namespace];
const dictVoc = {};
jsonData.forEach((obj)=>{
if (!dictVoc[obj.declaration]){
dictVoc[obj.declaration] = {
propertyVocabs: {}
};
}
const decos = JSON.parse(obj.dcs);
decos.forEach((dcs)=>{
if (dcs.name === 'Term' || patternToDetermineVocab.test(dcs.name)){
if (obj.property !== ''){
if (!dictVoc[obj.declaration].propertyVocabs[obj.property]){
dictVoc[obj.declaration].propertyVocabs[obj.property] = {};
}
if (dcs.name === 'Term'){
dictVoc[obj.declaration].propertyVocabs[obj.property].term = dcs.arguments[0].value;
}
else {
const extensionKey = dcs.name.split('Term_')[1];
dictVoc[obj.declaration].propertyVocabs[obj.property][extensionKey] = dcs.arguments[0].value;
}
}
else {
if (dcs.name === 'Term'){
dictVoc[obj.declaration].term = dcs.arguments[0].value;
}
else {
const extensionKey = dcs.name.split('Term_')[1];
dictVoc[obj.declaration][extensionKey] = dcs.arguments[0].value;
}
}
}
});

});
Object.keys(dictVoc).forEach((decl)=>{
if (dictVoc[decl].term){
strVoc += ` - ${decl}: ${dictVoc[decl].term}\n`;
const otherProps = Object.keys(dictVoc[decl]).filter((str)=>str!=='term' && str!=='propertyVocabs');
otherProps.forEach((key)=>{
strVoc += ` ${key}: ${dictVoc[decl][key]}\n`;
});
}
if (dictVoc[decl].propertyVocabs && Object.keys(dictVoc[decl].propertyVocabs).length>0){
if (!dictVoc[decl].term){
strVoc += ` - ${decl}: ${decl}\n`;
}
strVoc += ' properties:\n';
Object.keys(dictVoc[decl].propertyVocabs).forEach((prop)=>{
strVoc += ` - ${prop}: ${dictVoc[decl].propertyVocabs[prop].term}\n`;
const otherProps = Object.keys(dictVoc[decl].propertyVocabs[prop]).filter((str)=>str!=='term');
otherProps.forEach((key)=>{
strVoc += ` ${key}: ${dictVoc[decl].propertyVocabs[prop][key]}\n`;
});
});
}
});
data.push(strVoc);
});
return data;
}
/**
* Adds a key-value pair to a dictionary (object) if the key exists,
* or creates a new key with the provided value.
*
* @param {Object} dictionary - The dictionary (object) to which to add the key-value pair.
* @param {string} key - The key to add or update.
* @param {any} value - The value to add or update.
* @param {string} declaration - The target decl.
* @param {string} property - The target property.
* @param {string} mapElement - The target mapElement.
* @returns {Object} - constructed DCS Dict
* @private
*/
static constructDCSDictionary(dictionary, key, value, declaration,property,mapElement='') {
const val = {
declaration,
property,
mapElement,
dcs: JSON.stringify(value),
};
if (dictionary[key] && Array.isArray( dictionary[key])) {
dictionary[key].push(val);
} else {
dictionary[key] = [val];
}
return dictionary;
}

/**
* Throws an error if the decoractor command is invalid
* @param {ModelManager} validationModelManager the validation model manager
Expand Down Expand Up @@ -421,7 +666,7 @@ class DecoratorManager {

if (declaration.$class === `${MetaModelNamespace}.MapDeclaration`) {
if (target.mapElement) {
switch(target.mapElement) {
switch (target.mapElement) {
case 'KEY':
case 'VALUE':
this.applyDecoratorForMapElement(target.mapElement, target, declaration, type, decorator);
Expand Down Expand Up @@ -476,4 +721,4 @@ class DecoratorManager {
}
}

module.exports = DecoratorManager;
module.exports = DecoratorManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace [email protected]
@deco
enum Dummy {
@one
o One
}
@scc
scalar SSN extends String default="000-00-0000"

@M1
participant participantName identified by participantKey {
@M3
o String participantKey
}

@Dummy("term1",2)
asset assetName identified by assetKey {
o String assetKey
}

map mapName {
@deco(1)
o String
o String
}

@Term("Person Class")
@Editable
concept Person {
@Term("HI")
@Custom
@Form("inputType", "text")
@New
o String firstName
@Form("inputType", "text")
@New
o String lastName
@Term("some")
@Term_cus("con")
@Form("inputType", "textArea")
@New
o String bio
@Form("inputType", "text")
@New
o String ssn
}
Loading