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

sanketshevkar/fix-namespace-target-dcs #876

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,16 @@ class DecoratorExtractor {
}
class DecoratorManager {
+ ModelManager validate(decoratorCommandSet,ModelFile[]) throws Error
+ ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?,boolean?)
+ ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?,boolean?,boolean?)
+ ExtractDecoratorsResult extractDecorators(ModelManager,object,boolean,string)
+ void validateCommand(ModelManager,command)
+ Boolean falsyOrEqual(string||,string[])
+ void applyDecorator(decorated,string,newDecorator)
+ void executeCommand(string,declaration,command)
+ void executeNamespaceCommand(model,command)
+ void executeCommand(string,declaration,command,boolean?)
+ void executePropertyCommand(property,command)
+ void checkForNamespaceTargetAndApplyDecorator(declaration,string,decorator,target,boolean?)
+ Boolean checkForNamespaceTarget(boolean?)
}
+ string[] intersect()
+ boolean isUnversionedNamespaceEqual()
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 @@ -25,6 +25,9 @@
#


Version 3.16.8 {e3fd3aa83c0d4767f64efeb36640c9e0} 2024-07-09
- Added a new pathway for applying dcs target at namespace

Version 3.16.7 {8f455df1e788c4994f423d6e236bee21} 2024-05-01
- Added missing `strictQualifiedDateTimes` option to Serializer.fromJSON

Expand Down
4 changes: 4 additions & 0 deletions packages/concerto-core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ const Concerto = require('./lib/concerto');
// MetaModel
const MetaModel = require('./lib/introspect/metamodel');

// ConcertoCodes
const ConcertoCodes = require('./lib/concertoCodes');

// Version
/** @type {{ name: string, version: string }} */
const version = require('./package.json');
Expand Down Expand Up @@ -148,5 +151,6 @@ module.exports = {
DateTimeUtil,
Concerto,
MetaModel,
ConcertoCodes,
version
};
20 changes: 20 additions & 0 deletions packages/concerto-core/lib/concertoCodes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

//deprecation codes
const CONCERTO_DEPRECATION_001 = 'concerto-dep:001';

module.exports = { CONCERTO_DEPRECATION_001 };
76 changes: 71 additions & 5 deletions packages/concerto-core/lib/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const ModelUtil = require('./modelutil');
const { MetaModelNamespace } = require('@accordproject/concerto-metamodel');
const semver = require('semver');
const DecoratorExtractor = require('./decoratorextractor');
const { Warning, ErrorCodes } = require('@accordproject/concerto-util');
const ConcertoCodes = require('./concertoCodes');

// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
Expand Down Expand Up @@ -209,6 +211,7 @@ class DecoratorManager {
* @param {boolean} [options.validateCommands] - validate the decorator command set targets. Note that
* the validate option must also be true
* @param {boolean} [options.migrate] - migrate the decoratorCommandSet $class to match the dcs model version
* @param {boolean} [options.enableDcsNamespaceTarget] - flag to control applying namespace targeted decorators on top of the namespace instead of all declarations in that namespace
* @returns {ModelManager} a new model manager with the decorations applied
*/
static decorateModels(modelManager, decoratorCommandSet, options) {
Expand Down Expand Up @@ -245,7 +248,10 @@ class DecoratorManager {
decoratedAst.models.forEach((model) => {
model.declarations.forEach((decl) => {
decoratorCommandSet.commands.forEach((command) => {
this.executeCommand(model.namespace, decl, command);
this.executeCommand(model.namespace, decl, command, options?.enableDcsNamespaceTarget);
if(this.checkForNamespaceTarget(options?.enableDcsNamespaceTarget)) {
this.executeNamespaceCommand(model, command);
}
});
});
});
Expand Down Expand Up @@ -440,15 +446,35 @@ class DecoratorManager {
}
}

/**
* Executes a Command against a Model Namespace, adding
* decorators to the Namespace.
* @param {*} model the model
* @param {*} command the Command object from the dcs
* org.accordproject.decoratorcommands model
*/
static executeNamespaceCommand(model, command) {
const { target, decorator, type } = command;

if (Object.keys(target).length === 2 && target.namespace) {
const { name } = ModelUtil.parseNamespace( model.namespace );
// should we just compare with namespace??
if(this.falsyOrEqual(target.namespace, [model.namespace,name])) {
this.applyDecorator(model, type, decorator);
}
}
}

/**
* Executes a Command against a ClassDeclaration, adding
* decorators to the ClassDeclaration, or its properties, as required.
* @param {string} namespace the namespace for the declaration
* @param {*} declaration the class declaration
* @param {*} command the Command object from the
* @param {*} command the Command object from the dcs
* @param {boolean} [enableDcsNamespaceTarget] - flag to control applying namespace targeted decorators on top of the namespace instead of all declarations in that namespace
* org.accordproject.decoratorcommands model
*/
static executeCommand(namespace, declaration, command) {
static executeCommand(namespace, declaration, command, enableDcsNamespaceTarget) {
const { target, decorator, type } = command;
const { name } = ModelUtil.parseNamespace( namespace );
if (this.falsyOrEqual(target.namespace, [namespace,name]) &&
Expand All @@ -474,10 +500,10 @@ class DecoratorManager {
this.applyDecorator(declaration.value, type, decorator);
}
} else {
this.applyDecorator(declaration, type, decorator);
this.checkForNamespaceTargetAndApplyDecorator(declaration, type, decorator, target, enableDcsNamespaceTarget);
}
} else if (!(target.property || target.properties || target.type)) {
this.applyDecorator(declaration, type, decorator);
this.checkForNamespaceTargetAndApplyDecorator(declaration, type, decorator, target, enableDcsNamespaceTarget);
} else {
// scalars are declarations but do not have properties
if (declaration.properties) {
Expand Down Expand Up @@ -511,6 +537,46 @@ class DecoratorManager {
this.applyDecorator(property, type, decorator);
}
}

/**
* Checks if enableDcsNamespaceTarget or ENABLE_DCS_TARGET_NAMESPACE is enabled or not
* if enabled, applies the decorator on top of the namespace or else on all declarations
* within the namespace.
* @param {*} declaration the type to apply the decorator to
* @param {string} type the command type
* @param {*} decorator the decorator to add
* @param {*} target the target object for the decorator
* @param {boolean} [enableDcsNamespaceTarget] - flag to control applying namespace targeted decorators on top of the namespace instead of all declarations in that namespace
*/
static checkForNamespaceTargetAndApplyDecorator(declaration, type, decorator, target, enableDcsNamespaceTarget) {
if(this.checkForNamespaceTarget(enableDcsNamespaceTarget)) {
if (target.declaration) {
this.applyDecorator(declaration, type, decorator);
}
} else {
this.applyDecorator(declaration, type, decorator);
}
}

/**
* Checks if enableDcsNamespaceTarget or ENABLE_DCS_TARGET_NAMESPACE is enabled or not
* and print depreaction warning if not enabled and return boolean value as well
* @param {boolean} [enableDcsNamespaceTarget] - flag to control applying namespace targeted decorators on top of the namespace instead of all declarations in that namespace
* @returns {Boolean} true if either of the flags is enabled
*/
static checkForNamespaceTarget(enableDcsNamespaceTarget) {
if(enableDcsNamespaceTarget || process.env.ENABLE_DCS_NAMESPACE_TARGET === 'true') {
return true;
} else {
Warning.printDeprecationWarning(
'Functionality for namespace targeted Decorator Command Sets has beed changed. Using namespace targets to apply decorators on all declarations in a namespace will be deprecated soon.',
ErrorCodes.DEPRECATION_WARNING,
ConcertoCodes.CONCERTO_DEPRECATION_001,
'Please refer See https://concerto.accordproject.org/depreaction#001'
);
return false;
}
}
}

module.exports = DecoratorManager;
1 change: 1 addition & 0 deletions packages/concerto-core/lib/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ class ModelFile extends Decorated {
if (this.getModelManager().isStrict()){
throw new Error('Wilcard Imports are not permitted in strict mode.');
}
// Should we change this as well?
console.warn('DEPRECATED: Wilcard Imports are deprecated in this version of Concerto and will be removed in a future version.');
this.importWildcardNamespaces.push(imp.namespace);
break;
Expand Down
13 changes: 13 additions & 0 deletions packages/concerto-core/test/data/decoratorcommands/web.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@
"name" : "Address",
"arguments" : []
}
},
{
"$class" : "[email protected]",
"type" : "UPSERT",
"target" : {
"$class" : "[email protected]",
"namespace" : "test"
},
"decorator" : {
"$class" : "[email protected]",
"name" : "IsValid",
"arguments" : []
}
}
]
}
103 changes: 100 additions & 3 deletions packages/concerto-core/test/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const VocabularyManager= require('../../concerto-vocabulary/lib/vocabularymanage
const Printer= require('../../concerto-cto/lib/printer');

const chai = require('chai');
const { DEPRECATION_WARNING } = require('@accordproject/concerto-util/lib/errorcodes');
const { CONCERTO_DEPRECATION_001 } = require('../lib/concertoCodes');
require('chai').should();
chai.use(require('chai-things'));
chai.use(require('chai-as-promised'));
Expand Down Expand Up @@ -121,7 +123,7 @@ describe('DecoratorManager', () => {
decoratedModelManager.should.not.be.null;
});

it('should add decorator', async function() {
it('should add decorators that target declarations', async function() {
// load a model to decorate
const testModelManager = new ModelManager({strict:true});
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
Expand All @@ -138,10 +140,105 @@ describe('DecoratorManager', () => {
const decl = decoratedModelManager.getType('[email protected]');
decl.should.not.be.null;
decl.getDecorator('Editable').should.not.be.null;
});

/*
This test is target to the functionality wherein if there exists a namespace targeted decorator, it applies the decorator to
all the declarations within the namespace, which has been identified as bug and will be deprecated.
*/
it('should add decorators that target namespace and catch warning - behaviour to be deprecated', async function() {
// event listner to catch the warning
process.once('warning', (warning) => {
chai.expect(warning.message).to.be.equals('Functionality for namespace targeted Decorator Command Sets has beed changed. Using namespace targets to apply decorators on all declarations in a namespace will be deprecated soon.');
chai.expect(warning.name).to.be.equals(DEPRECATION_WARNING);
chai.expect(warning.code).to.be.equals(CONCERTO_DEPRECATION_001);
chai.expect(warning.detail).to.be.equals('Please refer See https://concerto.accordproject.org/depreaction#001');
});
// load a model to decorate
const testModelManager = new ModelManager({strict:true});
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'test.cto');

const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/web.json'), 'utf-8');
const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs),
{validate: true, validateCommands: true});

const modelFile = decoratedModelManager.getModelFile('[email protected]');
modelFile.should.not.be.null;
chai.expect(modelFile.getDecorator('IsValid')).to.be.null;

const ssnDecl = decoratedModelManager.getType('[email protected]');
ssnDecl.should.not.be.null;
ssnDecl.getDecorator('IsValid').should.not.be.null;

const decl = decoratedModelManager.getType('[email protected]');
decl.should.not.be.null;
decl.getDecorator('IsValid').should.not.be.null;
});

/*
This test is target to the functionality wherein if there exists a namespace targeted decorator, it applies the decorator to the
namespace, which is the new feature added can be accessed using the feature flag: ENABLE_DCS_NAMESPACE_TARGET.
*/
it('should add decorators that target namespace - updated behaviour using environment variable', async function() {
process.env.ENABLE_DCS_NAMESPACE_TARGET = 'true';
// load a model to decorate
const testModelManager = new ModelManager({strict:true});
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'test.cto');

const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/web.json'), 'utf-8');
const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs),
{validate: true, validateCommands: true});

const modelFile = decoratedModelManager.getModelFile('[email protected]');
modelFile.should.not.be.null;
modelFile.getDecorator('IsValid').should.not.be.null;

const ssnDecl = decoratedModelManager.getType('[email protected]');
ssnDecl.should.not.be.null;
chai.expect(ssnDecl.getDecorator('IsValid')).to.be.null;
process.env.ENABLE_DCS_NAMESPACE_TARGET = 'false';
});

/*
This test is target to the functionality wherein if there exists a namespace targeted decorator, it applies the decorator to the
namespace, which is the new feature added can be accessed using the option parameter: enableDcsNamespaceTarget.
*/
it('should add decorators that target namespace - updated behaviour using options parameter', async function() {
// load a model to decorate
const testModelManager = new ModelManager({strict:true});
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'test.cto');

const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/web.json'), 'utf-8');
const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs),
{validate: true, validateCommands: true, enableDcsNamespaceTarget: true});

const modelFile = decoratedModelManager.getModelFile('[email protected]');
modelFile.should.not.be.null;
modelFile.getDecorator('IsValid').should.not.be.null;

const ssnDecl = decoratedModelManager.getType('[email protected]');
ssnDecl.should.not.be.null;
chai.expect(ssnDecl.getDecorator('IsValid')).to.be.null;
});

it('should add decorators that target properties', async function() {
// load a model to decorate
const testModelManager = new ModelManager({strict:true});
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'test.cto');

const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/web.json'), 'utf-8');
const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs),
{validate: true, validateCommands: true});

const decl = decoratedModelManager.getType('[email protected]');
decl.should.not.be.null;

const firstNameProperty = decl.getProperty('firstName');
firstNameProperty.should.not.be.null;

const decoratorFormFirstName = firstNameProperty.getDecorator('Form');
decoratorFormFirstName.should.not.be.null;
decoratorFormFirstName.getArguments()[0].should.equal('inputType');
Expand Down Expand Up @@ -567,4 +664,4 @@ describe('DecoratorManager', () => {
});
});

});
});
6 changes: 5 additions & 1 deletion packages/concerto-util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ const ErrorCodes = require('./lib/errorcodes');
// NullUtil
const NullUtil = require('./lib/null');

// Warning
const Warning = require('./lib/warning');

module.exports = {
BaseException,
BaseFileException,
Expand All @@ -75,5 +78,6 @@ module.exports = {
Label,
Identifiers,
ErrorCodes,
NullUtil
NullUtil,
Warning
};
4 changes: 3 additions & 1 deletion packages/concerto-util/lib/errorcodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ const DEFAULT_VALIDATOR_EXCEPTION = 'DefaultValidatorException';
const REGEX_VALIDATOR_EXCEPTION = 'RegexValidatorException';
// base exception for Type not found
const TYPE_NOT_FOUND_EXCEPTION = 'TypeNotFoundException';
// deprecation warning type for process.emitWarning
const DEPRECATION_WARNING = 'DeprecationWarning';

module.exports = {DEFAULT_BASE_EXCEPTION, DEFAULT_VALIDATOR_EXCEPTION, REGEX_VALIDATOR_EXCEPTION, TYPE_NOT_FOUND_EXCEPTION};
module.exports = {DEFAULT_BASE_EXCEPTION, DEFAULT_VALIDATOR_EXCEPTION, REGEX_VALIDATOR_EXCEPTION, TYPE_NOT_FOUND_EXCEPTION, DEPRECATION_WARNING};
Loading
Loading