From 9c690b3e4b09590c9357e0a19231ad185b0c3136 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 2 Oct 2023 21:59:23 +0100 Subject: [PATCH 01/30] feat(dcs): add map type support Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 1136609dc..3a07b923e 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -50,6 +50,7 @@ concept CommandTarget { o String property optional o String[] properties optional // property and properties are mutually exclusive o String type optional + o String element optional } /** @@ -296,12 +297,58 @@ class DecoratorManager { */ static executeCommand(namespace, declaration, command) { const { target, decorator, type } = command; - const { name } = ModelUtil.parseNamespace(namespace); - if ( - this.falsyOrEqual(target.namespace, [namespace, name]) && - this.falsyOrEqual(target.declaration, [declaration.name]) - ) { - if (!target.property && !target.type) { + const { name } = ModelUtil.parseNamespace( namespace ); + if (this.falsyOrEqual(target.namespace, [namespace,name]) && + this.falsyOrEqual(target.declaration, [declaration.name])) { + + if (declaration.$class === 'concerto.metamodel@1.0.0.MapDeclaration') { + if (target.element) { + switch(target.element.toLowerCase()) { + case 'key': + if (target.type) { + if (this.falsyOrEqual(target.type, declaration.key.$class)) { + this.applyDecorator(declaration.key, type, decorator); + } + } else { + this.applyDecorator(declaration.key, type, decorator); + } + break; + case 'value': + if (target.type) { + if (this.falsyOrEqual(target.type, declaration.value.$class)) { + this.applyDecorator(declaration.value, type, decorator); + } + } else { + this.applyDecorator(declaration.key, type, decorator); + } + break; + case '*': + if (target.type) { + if (this.falsyOrEqual(target.type, declaration.key.$class)) { + this.applyDecorator(declaration.key, type, decorator); + } + if (this.falsyOrEqual(target.type, declaration.value.$class)) { + this.applyDecorator(declaration.value, type, decorator); + } + } else { + this.applyDecorator(declaration.key, type, decorator); + this.applyDecorator(declaration.value, type, decorator); + } + + break; + default: + throw new Error('Unrecognised Element value found: ' + target.element ); + } + + } else if (target.type) { + if (this.falsyOrEqual(target.type, declaration.key.$class)) { + this.applyDecorator(declaration.key, type, decorator); + } + if (this.falsyOrEqual(target.type, declaration.value.$class)) { + this.applyDecorator(declaration.value, type, decorator); + } + } + } else if (!target.property && !target.type) { this.applyDecorator(declaration, type, decorator); } else { // scalars are declarations but do not have properties From a074e5cb126850676f3ff86b5d8d2d2046a15361 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 2 Oct 2023 22:26:25 +0100 Subject: [PATCH 02/30] feat(dcs): add test coverage Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 3 +- .../invalid-target-element.json | 21 ++++ .../decoratorcommands/map-declaration.json | 112 +++++++++++++++++ .../test/data/decoratorcommands/test.cto | 11 ++ .../concerto-core/test/decoratormanager.js | 115 ++++++++++++++++++ 5 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json create mode 100644 packages/concerto-core/test/data/decoratorcommands/map-declaration.json diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 3a07b923e..8c51835bd 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -337,9 +337,8 @@ class DecoratorManager { break; default: - throw new Error('Unrecognised Element value found: ' + target.element ); + throw new Error('Decorator Command contains invalid target element: ' + target.element ); } - } else if (target.type) { if (this.falsyOrEqual(target.type, declaration.key.$class)) { this.applyDecorator(declaration.key, type, decorator); diff --git a/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json b/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json new file mode 100644 index 000000000..d111c3767 --- /dev/null +++ b/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json @@ -0,0 +1,21 @@ +{ + "$class" : "org.accordproject.decoratorcommands@0.3.0.DecoratorCommandSet", + "name" : "invalid-target-element", + "version": "1.0.0", + "commands" : [ + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "element" : "INVALID_ELEMENT" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Test" + } + } + ] +} diff --git a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json new file mode 100644 index 000000000..3aaed22c6 --- /dev/null +++ b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json @@ -0,0 +1,112 @@ +{ + "$class" : "org.accordproject.decoratorcommands@0.3.0.DecoratorCommandSet", + "name" : "web", + "version": "1.0.0", + "commands" : [ + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "element": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Foo", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapValueType", + "element": "VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bar", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "element": "*" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Baz", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesKeyByType", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapValueType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesValueByType", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesAllMapKeys", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapValueType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesAllMapValues", + "arguments" : [] + } + } + ] +} diff --git a/packages/concerto-core/test/data/decoratorcommands/test.cto b/packages/concerto-core/test/data/decoratorcommands/test.cto index 1595a6c50..abfd024f5 100644 --- a/packages/concerto-core/test/data/decoratorcommands/test.cto +++ b/packages/concerto-core/test/data/decoratorcommands/test.cto @@ -12,4 +12,15 @@ concept Person { o String address1 o String address2 o Integer zip + o Dictionary dictionary +} + +map Dictionary { + o String + o String +} + +map Rolodex { + o String + o String } \ No newline at end of file diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index ac31e6c41..ecda86163 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -145,6 +145,107 @@ describe('DecoratorManager', () => { (decoratorZipProperty ===null).should.be.true; }); + it('should decorate the specified element on the specified Map Declaration (Map Key)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + dictionary.should.not.be.null; + dictionary.key.getDecorator('Foo').should.not.be.null; + }); + + it('should decorate the specified type on the specified Map Declaration (Map Key)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + dictionary.should.not.be.null; + dictionary.key.getDecorator('DecoratesKeyByType').should.not.be.null; + }); + + it('should decorate the specified element on the specified Map Declaration (Map Value)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + + dictionary.should.not.be.null; + dictionary.value.getDecorator('Bar').should.not.be.null; + }); + + it('should decorate the specified type on the specified Map Declaration (Map Value)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + dictionary.should.not.be.null; + dictionary.value.getDecorator('DecoratesValueByType').should.not.be.null; + }); + + it('should decorate both Key and Value elements on the specified Map Declaration', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + + dictionary.should.not.be.null; + dictionary.key.getDecorator('Baz').should.not.be.null; + dictionary.value.getDecorator('Baz').should.not.be.null; + }); + + it('should decorate all Map Declaration Key and Value elements on the model when a declaration is not specified', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + const rolodex = decoratedModelManager.getType('test@1.0.0.Rolodex'); + + dictionary.should.not.be.null; + dictionary.key.getDecorator('DecoratesAllMapKeys').should.not.be.null; + dictionary.value.getDecorator('DecoratesAllMapValues').should.not.be.null; + + rolodex.should.not.be.null; + rolodex.key.getDecorator('DecoratesAllMapKeys').should.not.be.null; + rolodex.value.getDecorator('DecoratesAllMapValues').should.not.be.null; + }); + it('should fail with invalid command', async function() { // load a model to decorate const testModelManager = new ModelManager({strict:true}); @@ -243,6 +344,20 @@ describe('DecoratorManager', () => { {validate: true, validateCommands: true}); }).should.throw(/Decorator Command references both property and properties. You must either reference a single property or a list of properites./); }); + + it('should detect invalid target element', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/invalid-target-element.json', 'utf-8'); + + (() => { + DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + }).should.throw(/Decorator Command contains invalid target element: INVALID_ELEMENT/); + }); }); describe('#validate', function() { From 134c20bcf509ddc5391caf7a9fd7a5b5f6a5fd5d Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 2 Oct 2023 22:28:15 +0100 Subject: [PATCH 03/30] add env var for test runner Signed-off-by: Jonathan Casey --- packages/concerto-core/test/decoratormanager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index ecda86163..0036a74c3 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -26,6 +26,7 @@ chai.use(require('chai-as-promised')); describe('DecoratorManager', () => { beforeEach(() => { + process.env.ENABLE_MAP_TYPE = 'true'; // TODO Remove on release of MapType }); afterEach(() => { From da4fdcfd2f5146787eaa77598fa77411333d8939 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 2 Oct 2023 23:27:16 +0100 Subject: [PATCH 04/30] feat(dcs): fix bug Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 8c51835bd..d06460eb8 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -319,7 +319,7 @@ class DecoratorManager { this.applyDecorator(declaration.value, type, decorator); } } else { - this.applyDecorator(declaration.key, type, decorator); + this.applyDecorator(declaration.value, type, decorator); } break; case '*': From 02de4976c9524af62c0182fc6aa21e80a21322b6 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 2 Oct 2023 23:27:43 +0100 Subject: [PATCH 05/30] feat(dcs): add more test cases Signed-off-by: Jonathan Casey --- .../decoratorcommands/map-declaration.json | 61 +++++++++++++++++++ .../concerto-core/test/decoratormanager.js | 19 ++++++ 2 files changed, 80 insertions(+) diff --git a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json index 3aaed22c6..7c3d0fbe6 100644 --- a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json +++ b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json @@ -19,6 +19,21 @@ "arguments" : [] } }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "element": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Qux", + "arguments" : [] + } + }, { "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", "type" : "UPSERT", @@ -35,6 +50,21 @@ "arguments" : [] } }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "element": "VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Quux", + "arguments" : [] + } + }, { "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", "type" : "UPSERT", @@ -50,6 +80,37 @@ "arguments" : [] } }, + + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "element": "*" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bazola", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapValueType", + "element": "*" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bongo", + "arguments" : [] + } + }, { "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", "type" : "UPSERT", diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index 0036a74c3..a2112d745 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -159,6 +159,7 @@ describe('DecoratorManager', () => { const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); dictionary.should.not.be.null; dictionary.key.getDecorator('Foo').should.not.be.null; + dictionary.key.getDecorator('Qux').should.not.be.null; }); it('should decorate the specified type on the specified Map Declaration (Map Key)', async function() { @@ -190,6 +191,7 @@ describe('DecoratorManager', () => { dictionary.should.not.be.null; dictionary.value.getDecorator('Bar').should.not.be.null; + dictionary.value.getDecorator('Quux').should.not.be.null; }); it('should decorate the specified type on the specified Map Declaration (Map Value)', async function() { @@ -224,6 +226,23 @@ describe('DecoratorManager', () => { dictionary.value.getDecorator('Baz').should.not.be.null; }); + it('should decorate a Key and Value element on an unspecified Map Declaration when a type is specified (type takes precedence over element value \'*\')', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/map-declaration.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + + dictionary.should.not.be.null; + dictionary.key.getDecorator('Bazola').should.not.be.null; + dictionary.value.getDecorator('Bongo').should.not.be.null; + }); + it('should decorate all Map Declaration Key and Value elements on the model when a declaration is not specified', async function() { // load a model to decorate const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); From 33c17ba8be53f79ce16da41464b9f7e889214a24 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 3 Oct 2023 13:18:48 +0100 Subject: [PATCH 06/30] feat(dcs): add more test cov Signed-off-by: Jonathan Casey --- .../data/decoratorcommands/map-declaration.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json index 7c3d0fbe6..63ecc9b0f 100644 --- a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json +++ b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json @@ -3,6 +3,22 @@ "name" : "web", "version": "1.0.0", "commands" : [ + { + "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", + "type" : "APPEND", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "declaration" : "Dictionary", + "element": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "TEST", + "arguments" : [] + } + }, { "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", "type" : "UPSERT", From c57c1da489fd84051bcf397418f23666ad543b10 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 3 Oct 2023 14:43:44 +0100 Subject: [PATCH 07/30] feat(dcs): rename command property to mapElement Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 8 ++++---- .../invalid-target-element.json | 2 +- .../data/decoratorcommands/map-declaration.json | 16 ++++++++-------- packages/concerto-core/test/decoratormanager.js | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index d06460eb8..81b841b46 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -50,7 +50,7 @@ concept CommandTarget { o String property optional o String[] properties optional // property and properties are mutually exclusive o String type optional - o String element optional + o String mapElement optional } /** @@ -302,8 +302,8 @@ class DecoratorManager { this.falsyOrEqual(target.declaration, [declaration.name])) { if (declaration.$class === 'concerto.metamodel@1.0.0.MapDeclaration') { - if (target.element) { - switch(target.element.toLowerCase()) { + if (target.mapElement) { + switch(target.mapElement.toLowerCase()) { case 'key': if (target.type) { if (this.falsyOrEqual(target.type, declaration.key.$class)) { @@ -337,7 +337,7 @@ class DecoratorManager { break; default: - throw new Error('Decorator Command contains invalid target element: ' + target.element ); + throw new Error('Decorator Command contains invalid target for Map element: ' + target.mapElement ); } } else if (target.type) { if (this.falsyOrEqual(target.type, declaration.key.$class)) { diff --git a/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json b/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json index d111c3767..acce342c7 100644 --- a/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json +++ b/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json @@ -10,7 +10,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "declaration" : "Dictionary", - "element" : "INVALID_ELEMENT" + "mapElement" : "INVALID_ELEMENT" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", diff --git a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json index 63ecc9b0f..8f852db19 100644 --- a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json +++ b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json @@ -11,7 +11,7 @@ "namespace" : "test@1.0.0", "type" : "concerto.metamodel@1.0.0.StringMapKeyType", "declaration" : "Dictionary", - "element": "KEY" + "mapElement": "KEY" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -27,7 +27,7 @@ "namespace" : "test@1.0.0", "declaration" : "Dictionary", "type" : "concerto.metamodel@1.0.0.StringMapKeyType", - "element": "KEY" + "mapElement": "KEY" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -42,7 +42,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "declaration" : "Dictionary", - "element": "KEY" + "mapElement": "KEY" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -58,7 +58,7 @@ "namespace" : "test@1.0.0", "declaration" : "Dictionary", "type" : "concerto.metamodel@1.0.0.StringMapValueType", - "element": "VALUE" + "mapElement": "VALUE" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -73,7 +73,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "declaration" : "Dictionary", - "element": "VALUE" + "mapElement": "VALUE" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -88,7 +88,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "declaration" : "Dictionary", - "element": "*" + "mapElement": "*" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -104,7 +104,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "type" : "concerto.metamodel@1.0.0.StringMapKeyType", - "element": "*" + "mapElement": "*" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -119,7 +119,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "type" : "concerto.metamodel@1.0.0.StringMapValueType", - "element": "*" + "mapElement": "*" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index a2112d745..8a519bbf8 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -376,7 +376,7 @@ describe('DecoratorManager', () => { (() => { DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), {validate: true, validateCommands: true}); - }).should.throw(/Decorator Command contains invalid target element: INVALID_ELEMENT/); + }).should.throw(/Decorator Command contains invalid target for Map element: INVALID_ELEMENT/); }); }); From 3c0a0c1f92afcab5e416a845e9b3a88e41d08522 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 3 Oct 2023 15:45:56 +0100 Subject: [PATCH 08/30] feat(dcs): refactor apply decorator logic Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 81b841b46..63600ee14 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -238,6 +238,27 @@ class DecoratorManager { } } + + /** + * Applies a new decorator to the Map element + * @private + * @param {string} element the value to test + * @param {string} target the command target + * @param {*} declaration the map declaration + * @param {string} type the command type + * @param {*} newDecorator the decorator to add + */ + static applyDecoaratorForMapElement(element, target, declaration, type, newDecorator ) { + const decl = element.toLowerCase() === 'key' ? declaration.key : declaration.value; + if (target.type) { + if (this.falsyOrEqual(target.type, decl.$class)) { + this.applyDecorator(decl, type, newDecorator); + } + } else { + this.applyDecorator(decl, type, newDecorator); + } + } + /** * Compares two arrays. If the first argument is falsy * the function returns true. @@ -305,35 +326,14 @@ class DecoratorManager { if (target.mapElement) { switch(target.mapElement.toLowerCase()) { case 'key': - if (target.type) { - if (this.falsyOrEqual(target.type, declaration.key.$class)) { - this.applyDecorator(declaration.key, type, decorator); - } - } else { - this.applyDecorator(declaration.key, type, decorator); - } + this.applyDecoaratorForMapElement(target.mapElement, target, declaration, type, decorator); break; case 'value': - if (target.type) { - if (this.falsyOrEqual(target.type, declaration.value.$class)) { - this.applyDecorator(declaration.value, type, decorator); - } - } else { - this.applyDecorator(declaration.value, type, decorator); - } + this.applyDecoaratorForMapElement(target.mapElement, target, declaration, type, decorator); break; case '*': - if (target.type) { - if (this.falsyOrEqual(target.type, declaration.key.$class)) { - this.applyDecorator(declaration.key, type, decorator); - } - if (this.falsyOrEqual(target.type, declaration.value.$class)) { - this.applyDecorator(declaration.value, type, decorator); - } - } else { - this.applyDecorator(declaration.key, type, decorator); - this.applyDecorator(declaration.value, type, decorator); - } + this.applyDecoaratorForMapElement('key', target, declaration, type, decorator); + this.applyDecoaratorForMapElement('value', target, declaration, type, decorator); break; default: From 76baa659a4dd37380f8b50a4d5a6a320494f3f95 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 3 Oct 2023 15:48:37 +0100 Subject: [PATCH 09/30] feat(dcs): fix typo Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 63600ee14..c15b2b9f0 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -248,7 +248,7 @@ class DecoratorManager { * @param {string} type the command type * @param {*} newDecorator the decorator to add */ - static applyDecoaratorForMapElement(element, target, declaration, type, newDecorator ) { + static applyDecoratorForMapElement(element, target, declaration, type, newDecorator ) { const decl = element.toLowerCase() === 'key' ? declaration.key : declaration.value; if (target.type) { if (this.falsyOrEqual(target.type, decl.$class)) { @@ -326,14 +326,14 @@ class DecoratorManager { if (target.mapElement) { switch(target.mapElement.toLowerCase()) { case 'key': - this.applyDecoaratorForMapElement(target.mapElement, target, declaration, type, decorator); + this.applyDecoratorForMapElement(target.mapElement, target, declaration, type, decorator); break; case 'value': - this.applyDecoaratorForMapElement(target.mapElement, target, declaration, type, decorator); + this.applyDecoratorForMapElement(target.mapElement, target, declaration, type, decorator); break; case '*': - this.applyDecoaratorForMapElement('key', target, declaration, type, decorator); - this.applyDecoaratorForMapElement('value', target, declaration, type, decorator); + this.applyDecoratorForMapElement('key', target, declaration, type, decorator); + this.applyDecoratorForMapElement('value', target, declaration, type, decorator); break; default: From ea63a5fda324bcd9b056de429860cc3591f9fcd4 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 3 Oct 2023 15:51:45 +0100 Subject: [PATCH 10/30] feat(dcs): jsdoc Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index c15b2b9f0..e193cea63 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -242,7 +242,7 @@ class DecoratorManager { /** * Applies a new decorator to the Map element * @private - * @param {string} element the value to test + * @param {string} element the element to apply the decorator to * @param {string} target the command target * @param {*} declaration the map declaration * @param {string} type the command type From d51e5ef64c7409b55f1ebb82349887237ccbe520 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 4 Oct 2023 09:45:00 +0100 Subject: [PATCH 11/30] restart GH check Signed-off-by: Jonathan Casey From 90ebf47badc5a3acfc256d855a6288d3241f932e Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 4 Oct 2023 17:42:15 +0100 Subject: [PATCH 12/30] feat(dcs): refactor to use enum for target elements Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 30 +++++++++++-------- .../decoratorcommands/map-declaration.json | 6 ++-- .../concerto-core/test/decoratormanager.js | 14 --------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index e193cea63..905e4a7ca 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -50,7 +50,16 @@ concept CommandTarget { o String property optional o String[] properties optional // property and properties are mutually exclusive o String type optional - o String mapElement optional + o MapElement mapElement optional +} + +/** + * Map Declaration elements which might be used as a target + */ +enum MapElement { + o KEY + o VALUE + o KEY_VALUE } /** @@ -249,7 +258,7 @@ class DecoratorManager { * @param {*} newDecorator the decorator to add */ static applyDecoratorForMapElement(element, target, declaration, type, newDecorator ) { - const decl = element.toLowerCase() === 'key' ? declaration.key : declaration.value; + const decl = element === 'KEY' ? declaration.key : declaration.value; if (target.type) { if (this.falsyOrEqual(target.type, decl.$class)) { this.applyDecorator(decl, type, newDecorator); @@ -324,20 +333,15 @@ class DecoratorManager { if (declaration.$class === 'concerto.metamodel@1.0.0.MapDeclaration') { if (target.mapElement) { - switch(target.mapElement.toLowerCase()) { - case 'key': + switch(target.mapElement) { + case 'KEY': + case 'VALUE': this.applyDecoratorForMapElement(target.mapElement, target, declaration, type, decorator); break; - case 'value': - this.applyDecoratorForMapElement(target.mapElement, target, declaration, type, decorator); - break; - case '*': - this.applyDecoratorForMapElement('key', target, declaration, type, decorator); - this.applyDecoratorForMapElement('value', target, declaration, type, decorator); - + case 'KEY_VALUE': + this.applyDecoratorForMapElement('KEY', target, declaration, type, decorator); + this.applyDecoratorForMapElement('VALUE', target, declaration, type, decorator); break; - default: - throw new Error('Decorator Command contains invalid target for Map element: ' + target.mapElement ); } } else if (target.type) { if (this.falsyOrEqual(target.type, declaration.key.$class)) { diff --git a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json index 8f852db19..92db66dd9 100644 --- a/packages/concerto-core/test/data/decoratorcommands/map-declaration.json +++ b/packages/concerto-core/test/data/decoratorcommands/map-declaration.json @@ -88,7 +88,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "declaration" : "Dictionary", - "mapElement": "*" + "mapElement": "KEY_VALUE" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -104,7 +104,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "type" : "concerto.metamodel@1.0.0.StringMapKeyType", - "mapElement": "*" + "mapElement": "KEY_VALUE" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", @@ -119,7 +119,7 @@ "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", "namespace" : "test@1.0.0", "type" : "concerto.metamodel@1.0.0.StringMapValueType", - "mapElement": "*" + "mapElement": "KEY_VALUE" }, "decorator" : { "$class" : "concerto.metamodel@1.0.0.Decorator", diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index 8a519bbf8..526730195 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -364,20 +364,6 @@ describe('DecoratorManager', () => { {validate: true, validateCommands: true}); }).should.throw(/Decorator Command references both property and properties. You must either reference a single property or a list of properites./); }); - - it('should detect invalid target element', async function() { - // load a model to decorate - const testModelManager = new ModelManager({strict:true}); - const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); - testModelManager.addCTOModel(modelText, 'test.cto'); - - const dcs = fs.readFileSync('./test/data/decoratorcommands/invalid-target-element.json', 'utf-8'); - - (() => { - DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), - {validate: true, validateCommands: true}); - }).should.throw(/Decorator Command contains invalid target for Map element: INVALID_ELEMENT/); - }); }); describe('#validate', function() { From ec2a2d39cf50d27c29b0eacbe6b3c3efb4720dd9 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 4 Oct 2023 17:42:45 +0100 Subject: [PATCH 13/30] feat(dcs): remove unusued test fixture Signed-off-by: Jonathan Casey --- .../invalid-target-element.json | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json diff --git a/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json b/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json deleted file mode 100644 index acce342c7..000000000 --- a/packages/concerto-core/test/data/decoratorcommands/invalid-target-element.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$class" : "org.accordproject.decoratorcommands@0.3.0.DecoratorCommandSet", - "name" : "invalid-target-element", - "version": "1.0.0", - "commands" : [ - { - "$class" : "org.accordproject.decoratorcommands@0.3.0.Command", - "type" : "UPSERT", - "target" : { - "$class" : "org.accordproject.decoratorcommands@0.3.0.CommandTarget", - "namespace" : "test@1.0.0", - "declaration" : "Dictionary", - "mapElement" : "INVALID_ELEMENT" - }, - "decorator" : { - "$class" : "concerto.metamodel@1.0.0.Decorator", - "name" : "Test" - } - } - ] -} From 00b54e1493b9affff422475eb0a0e8997898a76f Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 4 Oct 2023 17:52:23 +0100 Subject: [PATCH 14/30] feat(dcs): reference MetaModelNamespace Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 905e4a7ca..a408088ce 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -18,6 +18,7 @@ const ModelManager = require('./modelmanager'); const Serializer = require('./serializer'); const Factory = require('./factory'); const ModelUtil = require('./modelutil'); +const { MetaModelNamespace } = require('@accordproject/concerto-metamodel'); const DCS_MODEL = `concerto version "^3.0.0" namespace org.accordproject.decoratorcommands@0.3.0 @@ -331,7 +332,7 @@ class DecoratorManager { if (this.falsyOrEqual(target.namespace, [namespace,name]) && this.falsyOrEqual(target.declaration, [declaration.name])) { - if (declaration.$class === 'concerto.metamodel@1.0.0.MapDeclaration') { + if (declaration.$class === `${MetaModelNamespace}.MapDeclaration`) { if (target.mapElement) { switch(target.mapElement) { case 'KEY': From 8aab8042bf28d025b6a613f8a3777dacf155f692 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 10 Oct 2023 15:26:17 +0100 Subject: [PATCH 15/30] feat(dcs): set decoratorcommands at version 0.3.0 Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 2 +- packages/concerto-core/test/decoratormanager.js | 2 +- packages/concerto-core/types/lib/decoratormanager.d.ts | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index a408088ce..c22490772 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -138,7 +138,7 @@ class DecoratorManager { validationModelManager.addModelFiles(modelManager.getModelFiles()); validationModelManager.addCTOModel( DCS_MODEL, - 'decoratorcommands@0.2.0.cto' + 'decoratorcommands@0.3.0.cto' ); const factory = new Factory(validationModelManager); const serializer = new Serializer(factory, validationModelManager); diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index 526730195..b6697ae51 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -226,7 +226,7 @@ describe('DecoratorManager', () => { dictionary.value.getDecorator('Baz').should.not.be.null; }); - it('should decorate a Key and Value element on an unspecified Map Declaration when a type is specified (type takes precedence over element value \'*\')', async function() { + it('should decorate a Key and Value element on an unspecified Map Declaration when a type is specified (type takes precedence over element value KEY_VALUE)', async function() { // load a model to decorate const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); diff --git a/packages/concerto-core/types/lib/decoratormanager.d.ts b/packages/concerto-core/types/lib/decoratormanager.d.ts index 480e9c816..dee531fd0 100644 --- a/packages/concerto-core/types/lib/decoratormanager.d.ts +++ b/packages/concerto-core/types/lib/decoratormanager.d.ts @@ -27,6 +27,16 @@ declare class DecoratorManager { * @param {*} command the decorator command */ static validateCommand(validationModelManager: ModelManager, command: any): void; + /** + * Applies a new decorator to the Map element + * @private + * @param {string} element the element to apply the decorator to + * @param {string} target the command target + * @param {*} declaration the map declaration + * @param {string} type the command type + * @param {*} newDecorator the decorator to add + */ + private static applyDecoratorForMapElement; /** * Compares two arrays. If the first argument is falsy * the function returns true. From 296ca11a8b6cf43683f878a50900139341c4d362 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 10 Oct 2023 15:51:15 +0100 Subject: [PATCH 16/30] feat(dcs): fix merge Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index a1243db71..8b5a58721 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -185,7 +185,6 @@ class DecoratorManager { const factory = new Factory(validationModelManager); const serializer = new Serializer(factory, validationModelManager); serializer.fromJSON(decoratorCommandSet); - const validationModelManager = DecoratorManager.validate(decoratorCommandSet, modelManager.getModelFiles()); if (options?.validateCommands) { decoratorCommandSet.commands.forEach((command) => { DecoratorManager.validateCommand( From 8fe17179fcdcb652fc41440d08d55d61c5e57917 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 16 Oct 2023 17:15:36 +0100 Subject: [PATCH 17/30] feat(map): auto migrates minor version Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 35 ++++ .../incompatible_version_dcs.json | 189 ++++++++++++++++++ .../concerto-core/test/decoratormanager.js | 33 +++ 3 files changed, 257 insertions(+) create mode 100644 packages/concerto-core/test/data/decoratorcommands/incompatible_version_dcs.json diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 8b5a58721..5d0339f75 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -19,6 +19,7 @@ const Serializer = require('./serializer'); const Factory = require('./factory'); const ModelUtil = require('./modelutil'); const { MetaModelNamespace } = require('@accordproject/concerto-metamodel'); +const semver = require('semver'); // Types needed for TypeScript generation. /* eslint-disable no-unused-vars */ @@ -28,6 +29,7 @@ if (global === undefined) { } /* eslint-enable no-unused-vars */ +const DCS_VERSION = '0.3.0'; const DCS_MODEL = `concerto version "^3.0.0" namespace org.accordproject.decoratorcommands@0.3.0 @@ -158,6 +160,30 @@ class DecoratorManager { return validationModelManager; } + /** + * Rewrites the $class property on decoratorCommandSet classes. + * @private + * @param {*} decoratorCommandSet the DecoratorCommandSet object + * @param {string} replacement the DCS version upgrade target + * @returns {object} the migrated DecoratorCommandSet object + + */ + static upMigrateMinorVersion(decoratorCommandSet, replacement) { + if (decoratorCommandSet instanceof Object) { + for (let key in decoratorCommandSet) { + if (key === '$class' && decoratorCommandSet[key].includes('org.accordproject.decoratorcommands')) { + // match on semver pattern + const versionPattern = /\d+\.\d+\.\d+/; + decoratorCommandSet[key] = decoratorCommandSet[key].replace(versionPattern, DCS_VERSION); + } + if (decoratorCommandSet[key] instanceof Object || decoratorCommandSet[key] instanceof Array) { + this.upMigrateMinorVersion(decoratorCommandSet[key], replacement); + } + } + } + return decoratorCommandSet; + } + /** * Applies all the decorator commands from the DecoratorCommandSet * to the ModelManager. @@ -171,6 +197,15 @@ class DecoratorManager { * @returns {ModelManager} a new model manager with the decorations applied */ static decorateModels(modelManager, decoratorCommandSet, options) { + + // get the version of the input decoratorCommandSet from its $class property + const inputVersion = ModelUtil.parseNamespace(decoratorCommandSet.$class.replace(/\.([^.]*)$/, '')).version; + + // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION + if (semver.lt(inputVersion, DCS_VERSION)) { + decoratorCommandSet = this.upMigrateMinorVersion(decoratorCommandSet, DCS_VERSION); + } + if (options?.validate) { const validationModelManager = new ModelManager({ strict: true, diff --git a/packages/concerto-core/test/data/decoratorcommands/incompatible_version_dcs.json b/packages/concerto-core/test/data/decoratorcommands/incompatible_version_dcs.json new file mode 100644 index 000000000..5af274713 --- /dev/null +++ b/packages/concerto-core/test/data/decoratorcommands/incompatible_version_dcs.json @@ -0,0 +1,189 @@ +{ + "$class" : "org.accordproject.decoratorcommands@0.2.0.DecoratorCommandSet", + "name" : "web", + "version": "1.0.0", + "commands" : [ + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "APPEND", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "declaration" : "Dictionary", + "mapElement": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "TEST", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "mapElement": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Foo", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "mapElement": "KEY" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Qux", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapValueType", + "mapElement": "VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bar", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "mapElement": "VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Quux", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "mapElement": "KEY_VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Baz", + "arguments" : [] + } + }, + + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType", + "mapElement": "KEY_VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bazola", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapValueType", + "mapElement": "KEY_VALUE" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "Bongo", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesKeyByType", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "declaration" : "Dictionary", + "type" : "concerto.metamodel@1.0.0.StringMapValueType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesValueByType", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapKeyType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesAllMapKeys", + "arguments" : [] + } + }, + { + "$class" : "org.accordproject.decoratorcommands@0.2.0.Command", + "type" : "UPSERT", + "target" : { + "$class" : "org.accordproject.decoratorcommands@0.2.0.CommandTarget", + "namespace" : "test@1.0.0", + "type" : "concerto.metamodel@1.0.0.StringMapValueType" + }, + "decorator" : { + "$class" : "concerto.metamodel@1.0.0.Decorator", + "name" : "DecoratesAllMapValues", + "arguments" : [] + } + } + ] +} diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index df87b0049..a4b49d7a2 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -192,6 +192,39 @@ describe('DecoratorManager', () => { dictionary.key.getDecorator('Qux').should.not.be.null; }); + it('should auto upgrade decoratorcommands $class minor version if it is below DCS_VERSION (asserts decorators are correctly applied)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + const dcs = fs.readFileSync('./test/data/decoratorcommands/incompatible_version_dcs.json', 'utf-8'); + const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), + {validate: true, validateCommands: true}); + + const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); + dictionary.should.not.be.null; + dictionary.key.getDecorator('Foo').should.not.be.null; + dictionary.key.getDecorator('Qux').should.not.be.null; + }); + + + it('should auto upgrade decoratorcommands $class minor version if it is below DCS_VERSION (asserts correct upgrade on DCS $class properties)', async function() { + // load a model to decorate + const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); + const modelText = fs.readFileSync('./test/data/decoratorcommands/test.cto', 'utf-8'); + testModelManager.addCTOModel(modelText, 'test.cto'); + + let dcs = fs.readFileSync('./test/data/decoratorcommands/incompatible_version_dcs.json', 'utf-8'); + dcs = DecoratorManager.upMigrateMinorVersion(JSON.parse(dcs), '0.3.0'); + + dcs.$class.should.equal('org.accordproject.decoratorcommands@0.3.0.DecoratorCommandSet'); + dcs.commands[0].$class.should.equal('org.accordproject.decoratorcommands@0.3.0.Command'); + dcs.commands[0].target.$class.should.equal('org.accordproject.decoratorcommands@0.3.0.CommandTarget'); + dcs.commands[0].target.type.should.equal('concerto.metamodel@1.0.0.StringMapKeyType'); // concerto metamodel $class does not change + dcs.commands[0].decorator.$class.should.equal('concerto.metamodel@1.0.0.Decorator'); // concerto metamodel $class does not change + }); + it('should decorate the specified type on the specified Map Declaration (Map Key)', async function() { // load a model to decorate const testModelManager = new ModelManager({strict:true, skipLocationNodes: true}); From 13a42429d32320f074b0f069fb38b55a50be17b4 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Mon, 16 Oct 2023 17:17:51 +0100 Subject: [PATCH 18/30] fix jsdoc format Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 5d0339f75..ddf642876 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -166,7 +166,6 @@ class DecoratorManager { * @param {*} decoratorCommandSet the DecoratorCommandSet object * @param {string} replacement the DCS version upgrade target * @returns {object} the migrated DecoratorCommandSet object - */ static upMigrateMinorVersion(decoratorCommandSet, replacement) { if (decoratorCommandSet instanceof Object) { From 2d09e533e58cadd871c3e8880735bfeaec87e5f8 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Tue, 17 Oct 2023 10:26:32 +0100 Subject: [PATCH 19/30] restart coveralls Signed-off-by: Jonathan Casey From 3bb1c2589e6a32ab1e602f0cca9769ce48413d78 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 15:40:08 +0100 Subject: [PATCH 20/30] feat(map): function rename Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index ddf642876..197eb6922 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -164,10 +164,10 @@ class DecoratorManager { * Rewrites the $class property on decoratorCommandSet classes. * @private * @param {*} decoratorCommandSet the DecoratorCommandSet object - * @param {string} replacement the DCS version upgrade target + * @param {string} version the DCS version upgrade target * @returns {object} the migrated DecoratorCommandSet object */ - static upMigrateMinorVersion(decoratorCommandSet, replacement) { + static migrateTo(decoratorCommandSet, version) { if (decoratorCommandSet instanceof Object) { for (let key in decoratorCommandSet) { if (key === '$class' && decoratorCommandSet[key].includes('org.accordproject.decoratorcommands')) { @@ -176,7 +176,7 @@ class DecoratorManager { decoratorCommandSet[key] = decoratorCommandSet[key].replace(versionPattern, DCS_VERSION); } if (decoratorCommandSet[key] instanceof Object || decoratorCommandSet[key] instanceof Array) { - this.upMigrateMinorVersion(decoratorCommandSet[key], replacement); + this.migrateTo(decoratorCommandSet[key], version); } } } @@ -202,7 +202,7 @@ class DecoratorManager { // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION if (semver.lt(inputVersion, DCS_VERSION)) { - decoratorCommandSet = this.upMigrateMinorVersion(decoratorCommandSet, DCS_VERSION); + decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); } if (options?.validate) { From 3cbe91be6d7444479d22a16c0df0e0e8f63348d2 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 15:43:54 +0100 Subject: [PATCH 21/30] feat(map): function rename in test Signed-off-by: Jonathan Casey --- packages/concerto-core/test/decoratormanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index a4b49d7a2..6ac8b336b 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -216,7 +216,7 @@ describe('DecoratorManager', () => { testModelManager.addCTOModel(modelText, 'test.cto'); let dcs = fs.readFileSync('./test/data/decoratorcommands/incompatible_version_dcs.json', 'utf-8'); - dcs = DecoratorManager.upMigrateMinorVersion(JSON.parse(dcs), '0.3.0'); + dcs = DecoratorManager.migrateTo(JSON.parse(dcs), '0.3.0'); dcs.$class.should.equal('org.accordproject.decoratorcommands@0.3.0.DecoratorCommandSet'); dcs.commands[0].$class.should.equal('org.accordproject.decoratorcommands@0.3.0.Command'); From cf48362e5b745a98c6fd116f24669cc5b8b1b08a Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:11:08 +0100 Subject: [PATCH 22/30] feat(map): drop regex in favour of util fun Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 197eb6922..c51516436 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -171,9 +171,10 @@ class DecoratorManager { if (decoratorCommandSet instanceof Object) { for (let key in decoratorCommandSet) { if (key === '$class' && decoratorCommandSet[key].includes('org.accordproject.decoratorcommands')) { - // match on semver pattern - const versionPattern = /\d+\.\d+\.\d+/; - decoratorCommandSet[key] = decoratorCommandSet[key].replace(versionPattern, DCS_VERSION); + const ns = ModelUtil.getNamespace(decoratorCommandSet.$class); + decoratorCommandSet[key] = decoratorCommandSet[key].replace( + ModelUtil.parseNamespace(ns).version, + DCS_VERSION); } if (decoratorCommandSet[key] instanceof Object || decoratorCommandSet[key] instanceof Array) { this.migrateTo(decoratorCommandSet[key], version); From e2cf40283eaa32e99f8f93a26b3f6b54475bd3c1 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:14:39 +0100 Subject: [PATCH 23/30] feat(map): drop regex in favour of util fun Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index c51516436..94e547603 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -199,7 +199,7 @@ class DecoratorManager { static decorateModels(modelManager, decoratorCommandSet, options) { // get the version of the input decoratorCommandSet from its $class property - const inputVersion = ModelUtil.parseNamespace(decoratorCommandSet.$class.replace(/\.([^.]*)$/, '')).version; + const inputVersion = ModelUtil.parseNamespace(ModelUtil.getNamespace(decoratorCommandSet.$class)).version; // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION if (semver.lt(inputVersion, DCS_VERSION)) { From 8b799d5b579d967a87db4db0c20f1082c08e62e6 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:43:25 +0100 Subject: [PATCH 24/30] feat(map): add client option to run migrate Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 94e547603..d4e19b69d 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -194,16 +194,19 @@ class DecoratorManager { * with respect to to decorator command set model * @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 * @returns {ModelManager} a new model manager with the decorations applied */ static decorateModels(modelManager, decoratorCommandSet, options) { - // get the version of the input decoratorCommandSet from its $class property - const inputVersion = ModelUtil.parseNamespace(ModelUtil.getNamespace(decoratorCommandSet.$class)).version; + if (options?.migrate) { + // get the version of the decoratorCommandSet from its $class property + const inputVersion = ModelUtil.parseNamespace(ModelUtil.getNamespace(decoratorCommandSet.$class)).version; - // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION - if (semver.lt(inputVersion, DCS_VERSION)) { - decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); + // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION + if (semver.lt(inputVersion, DCS_VERSION)) { + decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); + } } if (options?.validate) { From 5f8729e59708d15dd7161bbcf02945fbd25a26a7 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:46:44 +0100 Subject: [PATCH 25/30] feat(map): add typedefs Signed-off-by: Jonathan Casey --- packages/concerto-core/types/lib/decoratormanager.d.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/concerto-core/types/lib/decoratormanager.d.ts b/packages/concerto-core/types/lib/decoratormanager.d.ts index d2066eb7a..a6b644280 100644 --- a/packages/concerto-core/types/lib/decoratormanager.d.ts +++ b/packages/concerto-core/types/lib/decoratormanager.d.ts @@ -19,6 +19,14 @@ declare class DecoratorManager { * @throws {Error} throws an error if the decoratorCommandSet is invalid */ static validate(decoratorCommandSet: any, modelFiles?: ModelFile[]): ModelManager; + /** + * Rewrites the $class property on decoratorCommandSet classes. + * @private + * @param {*} decoratorCommandSet the DecoratorCommandSet object + * @param {string} version the DCS version upgrade target + * @returns {object} the migrated DecoratorCommandSet object + */ + private static migrateTo; /** * Applies all the decorator commands from the DecoratorCommandSet * to the ModelManager. @@ -29,11 +37,13 @@ declare class DecoratorManager { * with respect to to decorator command set model * @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 * @returns {ModelManager} a new model manager with the decorations applied */ static decorateModels(modelManager: ModelManager, decoratorCommandSet: any, options?: { validate?: boolean; validateCommands?: boolean; + migrate?: boolean; }): ModelManager; /** * Throws an error if the decoractor command is invalid From 07aed29d9e339d35f8cf8dcd11f0ab7d56a7adcf Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:48:10 +0100 Subject: [PATCH 26/30] feat(map): add changelog Signed-off-by: Jonathan Casey --- packages/concerto-core/api.txt | 2 +- packages/concerto-core/changelog.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index d3d29dac7..e7fb64ee6 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -56,7 +56,7 @@ class Concerto { + object setCurrentTime() class DecoratorManager { + ModelManager validate(decoratorCommandSet,ModelFile[]) throws Error - + ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?) + + ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?,boolean?) + void validateCommand(ModelManager,command) + Boolean falsyOrEqual(string||,string[]) + void applyDecorator(decorated,string,newDecorator) diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index 81a893ad0..9ec221b44 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,6 +24,9 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # +Version 3.13.1 {50cd09fa08a4fdc9ef30d72b483c808a} 2023-10-18 +- Add migrate option to DecoratorManager options + Version 3.13.1 {6b09c1c58abcc77eecbb44e375c2efb8} 2023-10-03 - Add enableMapType option to BaseModelManager options From c961db45219bbd30427a21254e437f142c358a7b Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 16:49:03 +0100 Subject: [PATCH 27/30] test(map): add option to test Signed-off-by: Jonathan Casey --- packages/concerto-core/test/decoratormanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index 6ac8b336b..99d8a6269 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -200,7 +200,7 @@ describe('DecoratorManager', () => { const dcs = fs.readFileSync('./test/data/decoratorcommands/incompatible_version_dcs.json', 'utf-8'); const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs), - {validate: true, validateCommands: true}); + {validate: true, validateCommands: true, migrate: true}); const dictionary = decoratedModelManager.getType('test@1.0.0.Dictionary'); dictionary.should.not.be.null; From 83f01a43c4754a062ca33d62d63b27d726e18d04 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Wed, 18 Oct 2023 17:06:36 +0100 Subject: [PATCH 28/30] chore: fix changelog Signed-off-by: Jonathan Casey --- packages/concerto-core/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index 4cc058cb2..6af538a7d 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,10 +24,10 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # -Version 3.13.3 {50cd09fa08a4fdc9ef30d72b483c808a} 2023-10-18 +Version 3.13.1 {f5a9a1ea6a64865843a3abb77798cbb0} 2023-10-18 - Add migrate option to DecoratorManager options -Version 3.13.2 {f435a20a00712e49c5cd32bc73ecb06a} 2023-10-03 +Version 3.13.1 {f435a20a00712e49c5cd32bc73ecb06a} 2023-10-03 - Add JSDoc for enableMapType option on ModelManager Version 3.13.1 {6b09c1c58abcc77eecbb44e375c2efb8} 2023-10-03 From 1cb7055dad6aa15da4e3ce8df5167db17f931e75 Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Thu, 19 Oct 2023 07:59:44 +0100 Subject: [PATCH 29/30] feat(map): checks major version before migration Signed-off-by: Jonathan Casey --- .../concerto-core/lib/decoratormanager.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index d4e19b69d..19f5a07a9 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -184,6 +184,19 @@ class DecoratorManager { return decoratorCommandSet; } + /** + * Checks if the supplied decoratorCommandSet can be migrated. + * Migrations should only take place across minor versions of the same major version. + * @private + * @param {*} decoratorCommandSet the DecoratorCommandSet object + * @param {*} DCS_VERSION the DecoratorCommandSet version + * @returns {boolean} returns true if major versions are equal + */ + static canMigrate(decoratorCommandSet, DCS_VERSION) { + const inputVersion = ModelUtil.parseNamespace(ModelUtil.getNamespace(decoratorCommandSet.$class)).version; + return (semver.major(inputVersion) === semver.major(DCS_VERSION) && (semver.minor(inputVersion) < semver.minor(DCS_VERSION))); + } + /** * Applies all the decorator commands from the DecoratorCommandSet * to the ModelManager. @@ -200,11 +213,7 @@ class DecoratorManager { static decorateModels(modelManager, decoratorCommandSet, options) { if (options?.migrate) { - // get the version of the decoratorCommandSet from its $class property - const inputVersion = ModelUtil.parseNamespace(ModelUtil.getNamespace(decoratorCommandSet.$class)).version; - - // if its < the currect DCS_Version, rewrite the $class version to match the supported DCS_VERSION - if (semver.lt(inputVersion, DCS_VERSION)) { + if (this.canMigrate(decoratorCommandSet, DCS_VERSION)) { decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); } } From 3e856ce28a957150b705f5a823f730a61833f4ee Mon Sep 17 00:00:00 2001 From: Jonathan Casey Date: Thu, 19 Oct 2023 08:19:13 +0100 Subject: [PATCH 30/30] feat(map): refactor conditional logic Signed-off-by: Jonathan Casey --- packages/concerto-core/lib/decoratormanager.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 19f5a07a9..ab684705f 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -212,10 +212,8 @@ class DecoratorManager { */ static decorateModels(modelManager, decoratorCommandSet, options) { - if (options?.migrate) { - if (this.canMigrate(decoratorCommandSet, DCS_VERSION)) { - decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); - } + if (options?.migrate && this.canMigrate(decoratorCommandSet, DCS_VERSION)) { + decoratorCommandSet = this.migrateTo(decoratorCommandSet, DCS_VERSION); } if (options?.validate) {