From c3ee097847565c3fb18e21e6f34e1d92eac14e4e Mon Sep 17 00:00:00 2001 From: sanketshevkar Date: Wed, 10 Jul 2024 11:30:17 +0530 Subject: [PATCH 1/3] fix(core): new pathway to apply namespace targeted decorators Signed-off-by: sanketshevkar --- packages/concerto-core/api.txt | 7 +- packages/concerto-core/changelog.txt | 3 + packages/concerto-core/index.js | 4 + packages/concerto-core/lib/concertoCodes.js | 20 ++++ .../concerto-core/lib/decoratormanager.js | 76 ++++++++++++- .../concerto-core/lib/introspect/modelfile.js | 1 + .../test/data/decoratorcommands/web.json | 13 +++ .../concerto-core/test/decoratormanager.js | 103 +++++++++++++++++- packages/concerto-util/index.js | 6 +- packages/concerto-util/lib/errorcodes.js | 4 +- packages/concerto-util/lib/warning.js | 40 +++++++ 11 files changed, 265 insertions(+), 12 deletions(-) create mode 100644 packages/concerto-core/lib/concertoCodes.js create mode 100644 packages/concerto-util/lib/warning.js diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 139c8c287..1576e1a7a 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -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() diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index d5e1b93ff..4b53e2cc6 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -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 diff --git a/packages/concerto-core/index.js b/packages/concerto-core/index.js index b8387461e..c5a3df149 100644 --- a/packages/concerto-core/index.js +++ b/packages/concerto-core/index.js @@ -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'); @@ -148,5 +151,6 @@ module.exports = { DateTimeUtil, Concerto, MetaModel, + ConcertoCodes, version }; diff --git a/packages/concerto-core/lib/concertoCodes.js b/packages/concerto-core/lib/concertoCodes.js new file mode 100644 index 000000000..d7780032e --- /dev/null +++ b/packages/concerto-core/lib/concertoCodes.js @@ -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 }; diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index fc7860093..ed83c6a19 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -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 */ @@ -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) { @@ -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); + } }); }); }); @@ -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]) && @@ -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) { @@ -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; diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index e95d31ad4..bb9922a94 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -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; diff --git a/packages/concerto-core/test/data/decoratorcommands/web.json b/packages/concerto-core/test/data/decoratorcommands/web.json index 96ee26837..00a4303d0 100644 --- a/packages/concerto-core/test/data/decoratorcommands/web.json +++ b/packages/concerto-core/test/data/decoratorcommands/web.json @@ -153,6 +153,19 @@ "name" : "Address", "arguments" : [] } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "IsValid", + "arguments" : [] + } } ] } diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index 20481c3f1..ebde57a59 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -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')); @@ -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'); @@ -138,10 +140,105 @@ describe('DecoratorManager', () => { const decl = decoratedModelManager.getType('test@1.0.0.Person'); 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('test@1.0.0'); + modelFile.should.not.be.null; + chai.expect(modelFile.getDecorator('IsValid')).to.be.null; + + const ssnDecl = decoratedModelManager.getType('test@1.0.0.SSN'); + ssnDecl.should.not.be.null; + ssnDecl.getDecorator('IsValid').should.not.be.null; + + const decl = decoratedModelManager.getType('test@1.0.0.Person'); + 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('test@1.0.0'); + modelFile.should.not.be.null; + modelFile.getDecorator('IsValid').should.not.be.null; + + const ssnDecl = decoratedModelManager.getType('test@1.0.0.SSN'); + 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('test@1.0.0'); + modelFile.should.not.be.null; + modelFile.getDecorator('IsValid').should.not.be.null; + + const ssnDecl = decoratedModelManager.getType('test@1.0.0.SSN'); + 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('test@1.0.0.Person'); + 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'); @@ -567,4 +664,4 @@ describe('DecoratorManager', () => { }); }); -}); \ No newline at end of file +}); diff --git a/packages/concerto-util/index.js b/packages/concerto-util/index.js index f1d3611dc..36ea61380 100644 --- a/packages/concerto-util/index.js +++ b/packages/concerto-util/index.js @@ -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, @@ -75,5 +78,6 @@ module.exports = { Label, Identifiers, ErrorCodes, - NullUtil + NullUtil, + Warning }; diff --git a/packages/concerto-util/lib/errorcodes.js b/packages/concerto-util/lib/errorcodes.js index 3f6ec0628..06e299631 100644 --- a/packages/concerto-util/lib/errorcodes.js +++ b/packages/concerto-util/lib/errorcodes.js @@ -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}; \ No newline at end of file +module.exports = {DEFAULT_BASE_EXCEPTION, DEFAULT_VALIDATOR_EXCEPTION, REGEX_VALIDATOR_EXCEPTION, TYPE_NOT_FOUND_EXCEPTION, DEPRECATION_WARNING}; diff --git a/packages/concerto-util/lib/warning.js b/packages/concerto-util/lib/warning.js new file mode 100644 index 000000000..5bce5bbef --- /dev/null +++ b/packages/concerto-util/lib/warning.js @@ -0,0 +1,40 @@ +/* + * 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'; + +let isWarningEmitted = false; + +/** + * Emits DeprecationWaring to stderr only once and can be caught using an warning event listener as well, please define the code + * and document the deprecation code on https://concerto.accordproject.org/depreaction + * @param {string} message - message of the deprecation warning + * @param {string} type - type of the deprecation warning + * @param {string} code - code of the deprecation warning + * @param {string} detail - detail of the deprecation warning + */ +function printDeprecationWarning(message, type, code, detail) { + if (!isWarningEmitted) { + isWarningEmitted = true; + process.emitWarning(message, { + type, + code, + detail + }); + } +} + +module.exports = { + printDeprecationWarning +}; From 68d7d57257a215013fe8f3b00b6ab12c2a96b12e Mon Sep 17 00:00:00 2001 From: sanketshevkar Date: Wed, 10 Jul 2024 15:17:57 +0530 Subject: [PATCH 2/3] fix(core): apply suggested changes Signed-off-by: sanketshevkar --- packages/concerto-core/api.txt | 3 -- packages/concerto-core/changelog.txt | 2 +- packages/concerto-core/index.js | 4 --- packages/concerto-core/lib/concertoCodes.js | 20 ----------- .../concerto-core/lib/decoratormanager.js | 17 ++++----- .../concerto-core/lib/introspect/modelfile.js | 9 +++-- .../concerto-core/test/decoratormanager.js | 7 ++-- .../types/lib/decoratormanager.d.ts | 35 ++++++++++++++++-- packages/concerto-util/lib/errorcodes.js | 7 +++- packages/concerto-util/lib/warning.js | 4 +-- packages/concerto-util/test/warning.js | 36 +++++++++++++++++++ packages/concerto-util/types/index.d.ts | 3 +- .../concerto-util/types/lib/errorcodes.d.ts | 3 ++ packages/concerto-util/types/lib/warning.d.ts | 9 +++++ 14 files changed, 111 insertions(+), 48 deletions(-) delete mode 100644 packages/concerto-core/lib/concertoCodes.js create mode 100644 packages/concerto-util/test/warning.js create mode 100644 packages/concerto-util/types/lib/warning.d.ts diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 1576e1a7a..0b9e4ed67 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -64,11 +64,8 @@ class DecoratorManager { + void validateCommand(ModelManager,command) + Boolean falsyOrEqual(string||,string[]) + void applyDecorator(decorated,string,newDecorator) - + 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() diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index 4b53e2cc6..445dfbea9 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -25,7 +25,7 @@ # -Version 3.16.8 {e3fd3aa83c0d4767f64efeb36640c9e0} 2024-07-09 +Version 3.16.8 {a23d37a4a92071314ff821f879943364} 2024-07-09 - Added a new pathway for applying dcs target at namespace Version 3.16.7 {8f455df1e788c4994f423d6e236bee21} 2024-05-01 diff --git a/packages/concerto-core/index.js b/packages/concerto-core/index.js index c5a3df149..b8387461e 100644 --- a/packages/concerto-core/index.js +++ b/packages/concerto-core/index.js @@ -102,9 +102,6 @@ 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'); @@ -151,6 +148,5 @@ module.exports = { DateTimeUtil, Concerto, MetaModel, - ConcertoCodes, version }; diff --git a/packages/concerto-core/lib/concertoCodes.js b/packages/concerto-core/lib/concertoCodes.js deleted file mode 100644 index d7780032e..000000000 --- a/packages/concerto-core/lib/concertoCodes.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 }; diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index ed83c6a19..6b2d81797 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -22,7 +22,6 @@ 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 */ @@ -249,7 +248,7 @@ class DecoratorManager { model.declarations.forEach((decl) => { decoratorCommandSet.commands.forEach((command) => { this.executeCommand(model.namespace, decl, command, options?.enableDcsNamespaceTarget); - if(this.checkForNamespaceTarget(options?.enableDcsNamespaceTarget)) { + if(this.isNamespaceTargetEnabled(options?.enableDcsNamespaceTarget)) { this.executeNamespaceCommand(model, command); } }); @@ -449,9 +448,9 @@ class DecoratorManager { /** * Executes a Command against a Model Namespace, adding * decorators to the Namespace. + * @private * @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; @@ -542,6 +541,7 @@ class DecoratorManager { * 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. + * @private * @param {*} declaration the type to apply the decorator to * @param {string} type the command type * @param {*} decorator the decorator to add @@ -549,7 +549,7 @@ class DecoratorManager { * @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(this.isNamespaceTargetEnabled(enableDcsNamespaceTarget)) { if (target.declaration) { this.applyDecorator(declaration, type, decorator); } @@ -560,19 +560,20 @@ class DecoratorManager { /** * 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 + * and print deprecation warning if not enabled and return boolean value as well + * @private * @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) { + static isNamespaceTargetEnabled(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' + ErrorCodes.CONCERTO_DEPRECATION_001, + 'Please refer to https://concerto.accordproject.org/deprecation/001' ); return false; } diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index bb9922a94..56ae29e28 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -31,6 +31,7 @@ const MapDeclaration = require('./mapdeclaration'); const ModelUtil = require('../modelutil'); const Globalize = require('../globalize'); const Decorated = require('./decorated'); +const { Warning, ErrorCodes } = require('@accordproject/concerto-util'); // Types needed for TypeScript generation. /* eslint-disable no-unused-vars */ @@ -746,8 +747,12 @@ 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.'); + Warning.printDeprecationWarning( + 'Wilcard Imports are deprecated in this version of Concerto and will be removed in a future version.', + ErrorCodes.DEPRECATION_WARNING, + ErrorCodes.CONCERTO_DEPRECATION_002, + 'Please refer to https://concerto.accordproject.org/deprecation/002' + ); this.importWildcardNamespaces.push(imp.namespace); break; case `${MetaModelNamespace}.ImportTypes`: diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index ebde57a59..f2a231742 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -22,8 +22,7 @@ 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'); +const { DEPRECATION_WARNING, CONCERTO_DEPRECATION_001 } = require('@accordproject/concerto-util/lib/errorcodes'); require('chai').should(); chai.use(require('chai-things')); chai.use(require('chai-as-promised')); @@ -149,10 +148,10 @@ describe('DecoratorManager', () => { 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.message).to.be.equals('DEPRECATED: 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'); + chai.expect(warning.detail).to.be.equals('Please refer to https://concerto.accordproject.org/deprecation/001'); }); // load a model to decorate const testModelManager = new ModelManager({strict:true}); diff --git a/packages/concerto-core/types/lib/decoratormanager.d.ts b/packages/concerto-core/types/lib/decoratormanager.d.ts index 3bab84250..c2e5fc229 100644 --- a/packages/concerto-core/types/lib/decoratormanager.d.ts +++ b/packages/concerto-core/types/lib/decoratormanager.d.ts @@ -47,12 +47,14 @@ declare 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: ModelManager, decoratorCommandSet: any, options?: { validate?: boolean; validateCommands?: boolean; migrate?: boolean; + enableDcsNamespaceTarget?: boolean; }): ModelManager; /** * @typedef decoratorCommandSet @@ -118,15 +120,24 @@ declare class DecoratorManager { * @param {*} newDecorator the decorator to add */ static applyDecorator(decorated: any, type: string, newDecorator: any): void; + /** + * Executes a Command against a Model Namespace, adding + * decorators to the Namespace. + * @private + * @param {*} model the model + * @param {*} command the Command object from the dcs + */ + private static executeNamespaceCommand; /** * 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: string, declaration: any, command: any): void; + static executeCommand(namespace: string, declaration: any, command: any, enableDcsNamespaceTarget?: boolean): void; /** * Executes a Command against a Property, adding * decorators to the Property as required. @@ -135,6 +146,26 @@ declare class DecoratorManager { * org.accordproject.decoratorcommands model */ static executePropertyCommand(property: any, command: any): void; + /** + * 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. + * @private + * @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 + */ + private static checkForNamespaceTargetAndApplyDecorator; + /** + * Checks if enableDcsNamespaceTarget or ENABLE_DCS_TARGET_NAMESPACE is enabled or not + * and print deprecation warning if not enabled and return boolean value as well + * @private + * @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 + */ + private static isNamespaceTargetEnabled; } import ModelFile = require("./introspect/modelfile"); import ModelManager = require("./modelmanager"); diff --git a/packages/concerto-util/lib/errorcodes.js b/packages/concerto-util/lib/errorcodes.js index 06e299631..9f56d6d72 100644 --- a/packages/concerto-util/lib/errorcodes.js +++ b/packages/concerto-util/lib/errorcodes.js @@ -25,4 +25,9 @@ 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, DEPRECATION_WARNING}; + +//deprecation codes +const CONCERTO_DEPRECATION_001 = 'concerto-dep:001'; +const CONCERTO_DEPRECATION_002 = 'concerto-dep:002'; + +module.exports = {DEFAULT_BASE_EXCEPTION, DEFAULT_VALIDATOR_EXCEPTION, REGEX_VALIDATOR_EXCEPTION, TYPE_NOT_FOUND_EXCEPTION, DEPRECATION_WARNING, CONCERTO_DEPRECATION_001, CONCERTO_DEPRECATION_002}; diff --git a/packages/concerto-util/lib/warning.js b/packages/concerto-util/lib/warning.js index 5bce5bbef..e2badca0e 100644 --- a/packages/concerto-util/lib/warning.js +++ b/packages/concerto-util/lib/warning.js @@ -18,7 +18,7 @@ let isWarningEmitted = false; /** * Emits DeprecationWaring to stderr only once and can be caught using an warning event listener as well, please define the code - * and document the deprecation code on https://concerto.accordproject.org/depreaction + * and document the deprecation code on https://concerto.accordproject.org/deprecation * @param {string} message - message of the deprecation warning * @param {string} type - type of the deprecation warning * @param {string} code - code of the deprecation warning @@ -27,7 +27,7 @@ let isWarningEmitted = false; function printDeprecationWarning(message, type, code, detail) { if (!isWarningEmitted) { isWarningEmitted = true; - process.emitWarning(message, { + process.emitWarning(`DEPRECATED: ${message}`, { type, code, detail diff --git a/packages/concerto-util/test/warning.js b/packages/concerto-util/test/warning.js new file mode 100644 index 000000000..8a4b907c5 --- /dev/null +++ b/packages/concerto-util/test/warning.js @@ -0,0 +1,36 @@ +/* + * 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'; + +const { expect } = require('chai'); +const { printDeprecationWarning } = require('../lib/warning'); + +describe('printDeprecationWarning', () => { + + it('should emit the correct deprecation warning message', () => { + const message = 'This feature is deprecated'; + const type = 'Deprecation'; + const code = 'DEP001'; + const detail = 'Please use the new feature instead'; + + process.once('warning', (warning) => { + expect(warning.message).to.be.equals(`DEPRECATED: ${message}`); + expect(warning.code).to.be.equals(code); + expect(warning.detail).to.be.equals(detail); + }); + + printDeprecationWarning(message, type, code, detail); + }); +}); \ No newline at end of file diff --git a/packages/concerto-util/types/index.d.ts b/packages/concerto-util/types/index.d.ts index e574c92da..10fa1e625 100644 --- a/packages/concerto-util/types/index.d.ts +++ b/packages/concerto-util/types/index.d.ts @@ -15,4 +15,5 @@ import Label = require("./lib/label"); import Identifiers = require("./lib/identifiers"); import ErrorCodes = require("./lib/errorcodes"); import NullUtil = require("./lib/null"); -export { BaseException, BaseFileException, FileDownloader, CompositeFileLoader, DefaultFileLoader, GitHubFileLoader, HTTPFileLoader, Writer, FileWriter, InMemoryWriter, ModelWriter, Logger, TypedStack, Label, Identifiers, ErrorCodes, NullUtil }; +import Warning = require("./lib/warning"); +export { BaseException, BaseFileException, FileDownloader, CompositeFileLoader, DefaultFileLoader, GitHubFileLoader, HTTPFileLoader, Writer, FileWriter, InMemoryWriter, ModelWriter, Logger, TypedStack, Label, Identifiers, ErrorCodes, NullUtil, Warning }; diff --git a/packages/concerto-util/types/lib/errorcodes.d.ts b/packages/concerto-util/types/lib/errorcodes.d.ts index 5bdae0591..b17c5a33a 100644 --- a/packages/concerto-util/types/lib/errorcodes.d.ts +++ b/packages/concerto-util/types/lib/errorcodes.d.ts @@ -2,3 +2,6 @@ export const DEFAULT_BASE_EXCEPTION: "DefaultBaseException"; export const DEFAULT_VALIDATOR_EXCEPTION: "DefaultValidatorException"; export const REGEX_VALIDATOR_EXCEPTION: "RegexValidatorException"; export const TYPE_NOT_FOUND_EXCEPTION: "TypeNotFoundException"; +export const DEPRECATION_WARNING: "DeprecationWarning"; +export const CONCERTO_DEPRECATION_001: "concerto-dep:001"; +export const CONCERTO_DEPRECATION_002: "concerto-dep:002"; diff --git a/packages/concerto-util/types/lib/warning.d.ts b/packages/concerto-util/types/lib/warning.d.ts new file mode 100644 index 000000000..68da8bac7 --- /dev/null +++ b/packages/concerto-util/types/lib/warning.d.ts @@ -0,0 +1,9 @@ +/** + * Emits DeprecationWaring to stderr only once and can be caught using an warning event listener as well, please define the code + * and document the deprecation code on https://concerto.accordproject.org/deprecation + * @param {string} message - message of the deprecation warning + * @param {string} type - type of the deprecation warning + * @param {string} code - code of the deprecation warning + * @param {string} detail - detail of the deprecation warning + */ +export function printDeprecationWarning(message: string, type: string, code: string, detail: string): void; From ab19c6b13e86385c5338eb5a3f2e61972ca3a8e7 Mon Sep 17 00:00:00 2001 From: sanketshevkar Date: Wed, 10 Jul 2024 21:00:53 +0530 Subject: [PATCH 3/3] fix(util): browser polyfill for process.emitWarning Signed-off-by: sanketshevkar --- packages/concerto-util/lib/warning.js | 4 +++- packages/concerto-util/webpack.config.js | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/concerto-util/lib/warning.js b/packages/concerto-util/lib/warning.js index e2badca0e..d9d9b4260 100644 --- a/packages/concerto-util/lib/warning.js +++ b/packages/concerto-util/lib/warning.js @@ -25,9 +25,11 @@ let isWarningEmitted = false; * @param {string} detail - detail of the deprecation warning */ function printDeprecationWarning(message, type, code, detail) { + // This will get pollyfilled in the webpack.config.js as process.emitWarning is not available in the browser + const customEmitWarning = process.emitWarning; if (!isWarningEmitted) { isWarningEmitted = true; - process.emitWarning(`DEPRECATED: ${message}`, { + customEmitWarning(`DEPRECATED: ${message}`, { type, code, detail diff --git a/packages/concerto-util/webpack.config.js b/packages/concerto-util/webpack.config.js index 4ab101832..1418c33a1 100644 --- a/packages/concerto-util/webpack.config.js +++ b/packages/concerto-util/webpack.config.js @@ -20,6 +20,8 @@ const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); const packageJson = require('./package.json'); +const emitWarningPollyfill = 'function(message, options){ console.warn({message: `DEPRECATED: ${message}`,type: options?.type,code: options?.code,detail: options?.detail});}'; + module.exports = { entry: './index.js', output: { @@ -44,7 +46,8 @@ module.exports = { new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify('production') - } + }, + 'process.emitWarning': emitWarningPollyfill }), new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'],