From f98734a3bff9481c32ea061916795d07c54b4f77 Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Thu, 19 Oct 2023 16:57:42 +0530 Subject: [PATCH 1/6] feat: added support for applying vocab and decorators to a model Signed-off-by: Muskan Bararia --- README.md | 16 +++++++++++++ index.js | 41 +++++++++++++++++++++++++++++++++ lib/commands.js | 44 ++++++++++++++++++++++++++++++++++- test/cli.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8455ce5..bed3b6b 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,22 @@ Options: --output output directory path [string] [default: "./"] ``` +### Concerto Decorate + +The `decorate` command allows you to add decorators and vocabularies to your local CTO file from a set of local DCS and Vocab Command Set files + +``` +concerto decorate + +apply the decorators and vocabs to the target models from given list of dcs files and vocab files + +Options: + --model The CTO file + --voc The List of vocab files + --dcs The List of decorator command set files + --f output format (cto or json), default: cto +``` + ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. diff --git a/index.js b/index.js index 3edf6f9..1fc70a4 100755 --- a/index.js +++ b/index.js @@ -408,6 +408,47 @@ require('yargs') Logger.error(err.message); }); }) + .command('decorate', 'apply the decorators and vocabs to the target models from given list of dcs files and vocab files', yargs => { + yargs.demandOption('model', 'Please provide a model'); + yargs.option('model', { + describe: 'The file location of the source model', + type: 'string', + }); + yargs.option('dcs', { + describe: 'List of dcs files to be applied to model', + type: 'string', + array:true + }); + yargs.option('voc', { + describe: 'List of vocab files to be applied to model', + type: 'string', + array:true + }); + yargs.option('f', { + describe: 'Output format (json or cto)', + type: 'string', + default:'cto', + choices: ['json', 'cto'] + }); + yargs.check((args) => { + // Custom validation to ensure at least one of the two options is provided + if (!args.dcs && !args.voc) { + throw new Error('You must provide at least one of dcs files or voc files'); + } + return true; + }); + }, argv => { + let options={}; + options.format=argv.f; + + return Commands.decorate(argv.model, argv.dcs,argv.voc,options) + .then(obj => { + console.log(obj); + }) + .catch((err) => { + Logger.error(err.message); + }); + }) .option('verbose', { alias: 'v', default: false diff --git a/lib/commands.js b/lib/commands.js index e01d2bb..bcdf42c 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -36,7 +36,8 @@ const Concerto = require('@accordproject/concerto-core').Concerto; const CodeGen = require('@accordproject/concerto-codegen').CodeGen; const { Compare, compareResultToString } = require('@accordproject/concerto-analysis'); -const { ModelFile, ModelManager } = require('@accordproject/concerto-core'); +const { ModelFile, ModelManager,DecoratorManager } = require('@accordproject/concerto-core'); +const { VocabularyManager } = require('@accordproject/concerto-vocabulary'); /** * Utility class that implements the commands exposed by the CLI. @@ -628,6 +629,47 @@ class Commands { inferredConcertoJsonModel.models[0] ); } + + /** + * Decorate a given model with given list of dcs and vocab files and print the result + * @param {string} modelFile - the model to which vocab and decorator has to be applied + * @param {string[]} dcsFiles - the decorator files to be applied to model + * @param {string[]} vocFiles - the vocab files to be applied to model + * @param {object} options - optional parameters + * @returns {string} - Depending on the options, CTO string or JSON string + */ + static async decorate(modelFile, dcsFiles,vocFiles,options) { + try { + const modelContent = fs.readFileSync(path.resolve(modelFile), 'utf-8'); + const allDCSFiles = dcsFiles ? dcsFiles.map(file => fs.readFileSync(file, 'utf8')) : []; + const allVocsFiles = vocFiles ? vocFiles.map(file => fs.readFileSync(file, 'utf8')) : []; + const modelManager = new ModelManager(); + modelManager.addModel(modelContent); + let updatedModelManager = modelManager; + if (allDCSFiles.length > 0) { + allDCSFiles.forEach(content => { + updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, JSON.parse(content)); + }); + } + if (allVocsFiles.length > 0) { + const vocManager = new VocabularyManager({ missingTermGenerator: VocabularyManager.englishMissingTermGenerator }); + allVocsFiles.forEach(content => { + vocManager.addVocabulary(content); + }); + const commandSet = vocManager.generateDecoratorCommands(updatedModelManager, 'en-gb'); + updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, commandSet); + } + const namespace = updatedModelManager.getModels().map(obj=>obj.name.replace(/\.cto$/, ''))[0]; + let modelAst = updatedModelManager.getModelFile(namespace).getAst(); + if (options.format === 'cto') { + return Printer.toCTO(modelAst); + } else { + return JSON.stringify(modelAst); + } + } catch (e) { + throw new Error(e); + } + } } module.exports = Commands; diff --git a/test/cli.js b/test/cli.js index 557f038..e848bb5 100644 --- a/test/cli.js +++ b/test/cli.js @@ -682,4 +682,65 @@ describe('concerto-cli', () => { expect(obj.fatherName.length >= 1).to.be.true; }); }); + + describe('#decorate', () => { + it('should apply the list of decorators to model and output in cto by default', async () => { + const dir = await tmp.dir({ unsafeCleanup: true }); + const model = (path.resolve(__dirname, 'models', 'decorate-model.cto')); + const decorators = [path.resolve(__dirname, 'data', 'decorate-dcs.json')]; + const vocabs = undefined; + const options={ + format:'cto' + }; + const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-dcs.cto'),'utf-8'); + const result =await Commands.decorate(model,decorators,vocabs,options); + result.replace(/[\r\n]+/g, '\n').should.equal(expected.replace(/[\r\n]+/g, '\n')); + dir.cleanup(); + }); + + it('should apply the list of vocabs to the model and output in cto by default', async () => { + const dir = await tmp.dir({ unsafeCleanup: true }); + const model = path.resolve(__dirname, 'models', 'decorate-model.cto'); + const vocabs = [path.resolve(__dirname, 'data', 'decorate-voc')]; + const decorators = undefined; + const options={ + format:'cto' + }; + const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-vocabs-only.cto'),'utf-8'); + const result =await Commands.decorate(model,decorators,vocabs,options); + result.replace(/[\r\n]+/g, '\n').should.equal(expected.replace(/[\r\n]+/g, '\n')); + dir.cleanup(); + }); + + it('should apply the list of vocabs and list of decorators to the model and output in asked format', async () => { + const dir = await tmp.dir({ unsafeCleanup: true }); + const model = path.resolve(__dirname, 'models', 'decorate-model.cto'); + const vocabs = [path.resolve(__dirname, 'data', 'decorate-voc')]; + const decorators = [path.resolve(__dirname, 'data', 'decorate-dcs.json')]; + const options={ + format:'json' + }; + const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-vocabs-and-deco.json'),'utf-8'); + const result =await Commands.decorate(model,decorators,vocabs,options); + result.should.equal(expected); + dir.cleanup(); + }); + it('should throw error if data is invalid', async () => { + const dir = await tmp.dir({ unsafeCleanup: true }); + const model = path.resolve(__dirname, 'models', 'decorate-invalid-model.cto'); + const vocabs = [path.resolve(__dirname, 'data', 'decorate-voc')]; + const decorators =undefined; + const options={ + format:'json' + }; + const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-vocabs-and-deco.json'),'utf-8'); + try { + const result =await Commands.decorate(model,decorators,vocabs,options); + result.should.eql(expected); + } catch (err) { + (typeof err).should.equal('object'); + } + dir.cleanup(); + }); + }); }); From 259264f5445a2105bd1a4071c5b85055d0185aa4 Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Thu, 19 Oct 2023 16:58:45 +0530 Subject: [PATCH 2/6] feat: added support for applying vocab and decorators to a model Signed-off-by: Muskan Bararia --- test/data/decorate-dcs.json | 37 +++++++++++++++++++ test/data/decorate-voc | 6 +++ test/models/decorate-invalid-model.cto | 0 .../decorate-model-expected-with-dcs.cto | 23 ++++++++++++ ...e-model-expected-with-vocabs-and-deco.json | 1 + ...corate-model-expected-with-vocabs-only.cto | 31 ++++++++++++++++ test/models/decorate-model.cto | 22 +++++++++++ 7 files changed, 120 insertions(+) create mode 100644 test/data/decorate-dcs.json create mode 100644 test/data/decorate-voc create mode 100644 test/models/decorate-invalid-model.cto create mode 100644 test/models/decorate-model-expected-with-dcs.cto create mode 100644 test/models/decorate-model-expected-with-vocabs-and-deco.json create mode 100644 test/models/decorate-model-expected-with-vocabs-only.cto create mode 100644 test/models/decorate-model.cto diff --git a/test/data/decorate-dcs.json b/test/data/decorate-dcs.json new file mode 100644 index 0000000..571ab8a --- /dev/null +++ b/test/data/decorate-dcs.json @@ -0,0 +1,37 @@ +{ + "$class": "org.accordproject.decoratorcommands.DecoratorCommandSet", + "name": "pii", + "version": "1.0.0", + "commands": [ + { + "$class": "org.accordproject.decoratorcommands.Command", + "type": "UPSERT", + "target": { + "$class": "org.accordproject.decoratorcommands.CommandTarget", + "property": "ssn" + }, + "decorator": { + "$class": "concerto.metamodel@1.0.0.Decorator", + "name": "PII", + "arguments": [ + ] + } + }, + { + "$class": "org.accordproject.decoratorcommands.Command", + "type": "UPSERT", + "target": { + "$class": "org.accordproject.decoratorcommands.CommandTarget", + "type": "concerto.metamodel@1.0.0.ObjectProperty" + }, + "decorator": { + "$class": "concerto.metamodel@1.0.0.Decorator", + "name": "Hide", + "arguments": [{ + "$class" : "concerto.metamodel@1.0.0.DecoratorString", + "value" : "object" + }] + } + } + ] + } \ No newline at end of file diff --git a/test/data/decorate-voc b/test/data/decorate-voc new file mode 100644 index 0000000..b2b9460 --- /dev/null +++ b/test/data/decorate-voc @@ -0,0 +1,6 @@ +locale: en-gb +namespace: test@1.0.0 +declarations: + - Driver: A driver of a vehicle + properties: + - favoriteColor: favourite colour \ No newline at end of file diff --git a/test/models/decorate-invalid-model.cto b/test/models/decorate-invalid-model.cto new file mode 100644 index 0000000..e69de29 diff --git a/test/models/decorate-model-expected-with-dcs.cto b/test/models/decorate-model-expected-with-dcs.cto new file mode 100644 index 0000000..9a87583 --- /dev/null +++ b/test/models/decorate-model-expected-with-dcs.cto @@ -0,0 +1,23 @@ +namespace test@1.0.0 + +abstract concept Person identified by ssn { + o String firstName + o String lastName + @PII() + o String ssn +} + +concept Driver extends Person { + o String favoriteColor +} + +concept Employee { + @PII() + o String ssn +} + +concept Car identified by vin { + o String vin + @Hide("object") + o Person owner +} \ No newline at end of file diff --git a/test/models/decorate-model-expected-with-vocabs-and-deco.json b/test/models/decorate-model-expected-with-vocabs-and-deco.json new file mode 100644 index 0000000..a232226 --- /dev/null +++ b/test/models/decorate-model-expected-with-vocabs-and-deco.json @@ -0,0 +1 @@ +{"$class":"concerto.metamodel@1.0.0.Model","decorators":[],"namespace":"test@1.0.0","imports":[],"declarations":[{"$class":"concerto.metamodel@1.0.0.ConceptDeclaration","name":"Person","isAbstract":true,"properties":[{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"firstName","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":74,"line":5,"column":5,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":98,"line":6,"column":5,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"First Name of the Person"}]}]},{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"lastName","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":98,"line":6,"column":5,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":119,"line":7,"column":3,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Last Name of the Person"}]}]},{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"ssn","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":119,"line":7,"column":3,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":133,"line":8,"column":1,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"PII","arguments":[]},{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Ssn of the Person"}]}]}],"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":24,"line":3,"column":1,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":134,"line":8,"column":2,"$class":"concerto.metamodel@1.0.0.Position"}},"identified":{"$class":"concerto.metamodel@1.0.0.IdentifiedBy","name":"ssn"},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Person"}]}]},{"$class":"concerto.metamodel@1.0.0.ConceptDeclaration","name":"Driver","isAbstract":false,"properties":[{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"favoriteColor","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":173,"line":11,"column":3,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":197,"line":12,"column":1,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"favourite colour"}]}]}],"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":138,"line":10,"column":1,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":198,"line":12,"column":2,"$class":"concerto.metamodel@1.0.0.Position"}},"superType":{"$class":"concerto.metamodel@1.0.0.TypeIdentifier","name":"Person","namespace":"test@1.0.0"},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"A driver of a vehicle"}]}]},{"$class":"concerto.metamodel@1.0.0.ConceptDeclaration","name":"Employee","isAbstract":false,"properties":[{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"ssn","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":224,"line":15,"column":3,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":238,"line":16,"column":1,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"PII","arguments":[]},{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Ssn of the Employee"}]}]}],"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":202,"line":14,"column":1,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":239,"line":16,"column":2,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Employee"}]}]},{"$class":"concerto.metamodel@1.0.0.ConceptDeclaration","name":"Car","isAbstract":false,"properties":[{"$class":"concerto.metamodel@1.0.0.StringProperty","name":"vin","isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":281,"line":20,"column":5,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":299,"line":21,"column":5,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Vin of the Car"}]}]},{"$class":"concerto.metamodel@1.0.0.ObjectProperty","name":"owner","type":{"$class":"concerto.metamodel@1.0.0.TypeIdentifier","name":"Person","namespace":"test@1.0.0"},"isArray":false,"isOptional":false,"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":299,"line":21,"column":5,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":315,"line":22,"column":1,"$class":"concerto.metamodel@1.0.0.Position"}},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Hide","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"object"}]},{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Owner of the Car"}]}]}],"location":{"$class":"concerto.metamodel@1.0.0.Range","start":{"offset":243,"line":18,"column":1,"$class":"concerto.metamodel@1.0.0.Position"},"end":{"offset":316,"line":22,"column":2,"$class":"concerto.metamodel@1.0.0.Position"}},"identified":{"$class":"concerto.metamodel@1.0.0.IdentifiedBy","name":"vin"},"decorators":[{"$class":"concerto.metamodel@1.0.0.Decorator","name":"Term","arguments":[{"$class":"concerto.metamodel@1.0.0.DecoratorString","value":"Car"}]}]}]} \ No newline at end of file diff --git a/test/models/decorate-model-expected-with-vocabs-only.cto b/test/models/decorate-model-expected-with-vocabs-only.cto new file mode 100644 index 0000000..bcaa6f4 --- /dev/null +++ b/test/models/decorate-model-expected-with-vocabs-only.cto @@ -0,0 +1,31 @@ +namespace test@1.0.0 + +@Term("Person") +abstract concept Person identified by ssn { + @Term("First Name of the Person") + o String firstName + @Term("Last Name of the Person") + o String lastName + @Term("Ssn of the Person") + o String ssn +} + +@Term("A driver of a vehicle") +concept Driver extends Person { + @Term("favourite colour") + o String favoriteColor +} + +@Term("Employee") +concept Employee { + @Term("Ssn of the Employee") + o String ssn +} + +@Term("Car") +concept Car identified by vin { + @Term("Vin of the Car") + o String vin + @Term("Owner of the Car") + o Person owner +} \ No newline at end of file diff --git a/test/models/decorate-model.cto b/test/models/decorate-model.cto new file mode 100644 index 0000000..dcaa43e --- /dev/null +++ b/test/models/decorate-model.cto @@ -0,0 +1,22 @@ +namespace test@1.0.0 + +abstract concept Person identified by ssn +{ + o String firstName + o String lastName + o String ssn +} + +concept Driver extends Person { + o String favoriteColor +} + +concept Employee { + o String ssn +} + +concept Car identified by vin +{ + o String vin + o Person owner +} \ No newline at end of file From 3976284682c8581dfa997feb1efae78afd15d8a3 Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Thu, 19 Oct 2023 19:33:04 +0530 Subject: [PATCH 3/6] feat: Added support for output and standardised arg params Signed-off-by: Muskan Bararia --- README.md | 7 ++++--- index.js | 17 +++++++++++------ lib/commands.js | 40 +++++++++++++++++++--------------------- test/cli.js | 16 +++++++++++++++- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index bed3b6b..b23c75f 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,10 @@ apply the decorators and vocabs to the target models from given list of dcs file Options: --model The CTO file - --voc The List of vocab files - --dcs The List of decorator command set files - --f output format (cto or json), default: cto + --vocabulary The List of vocab files + --decorator The List of decorator command set files + --format output format (cto or json), default: cto + --output output directory path ``` ## License diff --git a/index.js b/index.js index 1fc70a4..91fc2f2 100755 --- a/index.js +++ b/index.js @@ -414,34 +414,39 @@ require('yargs') describe: 'The file location of the source model', type: 'string', }); - yargs.option('dcs', { + yargs.option('decorator', { describe: 'List of dcs files to be applied to model', type: 'string', array:true }); - yargs.option('voc', { + yargs.option('vocabulary', { describe: 'List of vocab files to be applied to model', type: 'string', array:true }); - yargs.option('f', { + yargs.option('format', { describe: 'Output format (json or cto)', type: 'string', default:'cto', choices: ['json', 'cto'] }); + yargs.option('output', { + describe: 'output directory path', + type: 'string', + }); yargs.check((args) => { // Custom validation to ensure at least one of the two options is provided - if (!args.dcs && !args.voc) { + if (!args.decorator && !args.vocabulary) { throw new Error('You must provide at least one of dcs files or voc files'); } return true; }); }, argv => { let options={}; - options.format=argv.f; + options.format=argv.format; + options.output=argv.output; - return Commands.decorate(argv.model, argv.dcs,argv.voc,options) + return Commands.decorate(argv.model, argv.decorator,argv.vocabulary,options) .then(obj => { console.log(obj); }) diff --git a/lib/commands.js b/lib/commands.js index bcdf42c..69c8263 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -643,29 +643,27 @@ class Commands { const modelContent = fs.readFileSync(path.resolve(modelFile), 'utf-8'); const allDCSFiles = dcsFiles ? dcsFiles.map(file => fs.readFileSync(file, 'utf8')) : []; const allVocsFiles = vocFiles ? vocFiles.map(file => fs.readFileSync(file, 'utf8')) : []; - const modelManager = new ModelManager(); + let modelManager = new ModelManager(); modelManager.addModel(modelContent); - let updatedModelManager = modelManager; - if (allDCSFiles.length > 0) { - allDCSFiles.forEach(content => { - updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, JSON.parse(content)); - }); - } - if (allVocsFiles.length > 0) { - const vocManager = new VocabularyManager({ missingTermGenerator: VocabularyManager.englishMissingTermGenerator }); - allVocsFiles.forEach(content => { - vocManager.addVocabulary(content); - }); - const commandSet = vocManager.generateDecoratorCommands(updatedModelManager, 'en-gb'); - updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, commandSet); - } - const namespace = updatedModelManager.getModels().map(obj=>obj.name.replace(/\.cto$/, ''))[0]; - let modelAst = updatedModelManager.getModelFile(namespace).getAst(); - if (options.format === 'cto') { - return Printer.toCTO(modelAst); - } else { - return JSON.stringify(modelAst); + allDCSFiles.forEach(content => { + modelManager = DecoratorManager.decorateModels(modelManager, JSON.parse(content)); + }); + const vocManager = new VocabularyManager({ missingTermGenerator: VocabularyManager.englishMissingTermGenerator }); + const namespace = modelManager.getModels().map(obj=>obj.name.replace(/\.cto$/, ''))[0]; + allVocsFiles.forEach(content => { + vocManager.addVocabulary(content); + }); + const vocabKeySet=(Object.keys(vocManager.vocabularies)).map(key=>key.replace(new RegExp(`^${namespace}/`), '')); + vocabKeySet.map(voc=>{ + let commandSet = vocManager.generateDecoratorCommands(modelManager, voc); + modelManager = DecoratorManager.decorateModels(modelManager, commandSet); + }); + let modelAst = modelManager.getModelFile(namespace).getAst(); + let data = (options.format === 'cto') ? Printer.toCTO(modelAst):JSON.stringify(modelAst); + if (options.output) { + fs.writeFileSync(options.output, data); } + return data; } catch (e) { throw new Error(e); } diff --git a/test/cli.js b/test/cli.js index e848bb5..ea9f5cb 100644 --- a/test/cli.js +++ b/test/cli.js @@ -722,7 +722,7 @@ describe('concerto-cli', () => { }; const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-vocabs-and-deco.json'),'utf-8'); const result =await Commands.decorate(model,decorators,vocabs,options); - result.should.equal(expected); + result.trim().should.equal(expected.trim()); dir.cleanup(); }); it('should throw error if data is invalid', async () => { @@ -742,5 +742,19 @@ describe('concerto-cli', () => { } dir.cleanup(); }); + it('should write to a file if output is provided', async () => { + const output = await tmp.file({ unsafeCleanup: true }); + const model = path.resolve(__dirname, 'models', 'decorate-model.cto'); + const vocabs = [path.resolve(__dirname, 'data', 'decorate-voc')]; + const decorators =undefined; + const options={ + format:'json', + output:output.path + }; + const expected = await Commands.decorate(model,decorators,vocabs,options); + const result = fs.readFileSync(output.path, 'utf-8'); + result.trim().should.equal(expected.trim()); + output.cleanup(); + }); }); }); From 5975b8a3b62b9babcacb1df09fcf482f3d96da5b Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Thu, 19 Oct 2023 19:33:04 +0530 Subject: [PATCH 4/6] feat: Added support for output and standardised arg params Signed-off-by: Muskan Bararia --- README.md | 7 ++++--- index.js | 17 +++++++++++------ lib/commands.js | 40 +++++++++++++++++++--------------------- test/cli.js | 16 +++++++++++++++- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index bed3b6b..b23c75f 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,10 @@ apply the decorators and vocabs to the target models from given list of dcs file Options: --model The CTO file - --voc The List of vocab files - --dcs The List of decorator command set files - --f output format (cto or json), default: cto + --vocabulary The List of vocab files + --decorator The List of decorator command set files + --format output format (cto or json), default: cto + --output output directory path ``` ## License diff --git a/index.js b/index.js index 1fc70a4..91fc2f2 100755 --- a/index.js +++ b/index.js @@ -414,34 +414,39 @@ require('yargs') describe: 'The file location of the source model', type: 'string', }); - yargs.option('dcs', { + yargs.option('decorator', { describe: 'List of dcs files to be applied to model', type: 'string', array:true }); - yargs.option('voc', { + yargs.option('vocabulary', { describe: 'List of vocab files to be applied to model', type: 'string', array:true }); - yargs.option('f', { + yargs.option('format', { describe: 'Output format (json or cto)', type: 'string', default:'cto', choices: ['json', 'cto'] }); + yargs.option('output', { + describe: 'output directory path', + type: 'string', + }); yargs.check((args) => { // Custom validation to ensure at least one of the two options is provided - if (!args.dcs && !args.voc) { + if (!args.decorator && !args.vocabulary) { throw new Error('You must provide at least one of dcs files or voc files'); } return true; }); }, argv => { let options={}; - options.format=argv.f; + options.format=argv.format; + options.output=argv.output; - return Commands.decorate(argv.model, argv.dcs,argv.voc,options) + return Commands.decorate(argv.model, argv.decorator,argv.vocabulary,options) .then(obj => { console.log(obj); }) diff --git a/lib/commands.js b/lib/commands.js index bcdf42c..69c8263 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -643,29 +643,27 @@ class Commands { const modelContent = fs.readFileSync(path.resolve(modelFile), 'utf-8'); const allDCSFiles = dcsFiles ? dcsFiles.map(file => fs.readFileSync(file, 'utf8')) : []; const allVocsFiles = vocFiles ? vocFiles.map(file => fs.readFileSync(file, 'utf8')) : []; - const modelManager = new ModelManager(); + let modelManager = new ModelManager(); modelManager.addModel(modelContent); - let updatedModelManager = modelManager; - if (allDCSFiles.length > 0) { - allDCSFiles.forEach(content => { - updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, JSON.parse(content)); - }); - } - if (allVocsFiles.length > 0) { - const vocManager = new VocabularyManager({ missingTermGenerator: VocabularyManager.englishMissingTermGenerator }); - allVocsFiles.forEach(content => { - vocManager.addVocabulary(content); - }); - const commandSet = vocManager.generateDecoratorCommands(updatedModelManager, 'en-gb'); - updatedModelManager = DecoratorManager.decorateModels(updatedModelManager, commandSet); - } - const namespace = updatedModelManager.getModels().map(obj=>obj.name.replace(/\.cto$/, ''))[0]; - let modelAst = updatedModelManager.getModelFile(namespace).getAst(); - if (options.format === 'cto') { - return Printer.toCTO(modelAst); - } else { - return JSON.stringify(modelAst); + allDCSFiles.forEach(content => { + modelManager = DecoratorManager.decorateModels(modelManager, JSON.parse(content)); + }); + const vocManager = new VocabularyManager({ missingTermGenerator: VocabularyManager.englishMissingTermGenerator }); + const namespace = modelManager.getModels().map(obj=>obj.name.replace(/\.cto$/, ''))[0]; + allVocsFiles.forEach(content => { + vocManager.addVocabulary(content); + }); + const vocabKeySet=(Object.keys(vocManager.vocabularies)).map(key=>key.replace(new RegExp(`^${namespace}/`), '')); + vocabKeySet.map(voc=>{ + let commandSet = vocManager.generateDecoratorCommands(modelManager, voc); + modelManager = DecoratorManager.decorateModels(modelManager, commandSet); + }); + let modelAst = modelManager.getModelFile(namespace).getAst(); + let data = (options.format === 'cto') ? Printer.toCTO(modelAst):JSON.stringify(modelAst); + if (options.output) { + fs.writeFileSync(options.output, data); } + return data; } catch (e) { throw new Error(e); } diff --git a/test/cli.js b/test/cli.js index e848bb5..ea9f5cb 100644 --- a/test/cli.js +++ b/test/cli.js @@ -722,7 +722,7 @@ describe('concerto-cli', () => { }; const expected = fs.readFileSync(path.resolve(__dirname, 'models', 'decorate-model-expected-with-vocabs-and-deco.json'),'utf-8'); const result =await Commands.decorate(model,decorators,vocabs,options); - result.should.equal(expected); + result.trim().should.equal(expected.trim()); dir.cleanup(); }); it('should throw error if data is invalid', async () => { @@ -742,5 +742,19 @@ describe('concerto-cli', () => { } dir.cleanup(); }); + it('should write to a file if output is provided', async () => { + const output = await tmp.file({ unsafeCleanup: true }); + const model = path.resolve(__dirname, 'models', 'decorate-model.cto'); + const vocabs = [path.resolve(__dirname, 'data', 'decorate-voc')]; + const decorators =undefined; + const options={ + format:'json', + output:output.path + }; + const expected = await Commands.decorate(model,decorators,vocabs,options); + const result = fs.readFileSync(output.path, 'utf-8'); + result.trim().should.equal(expected.trim()); + output.cleanup(); + }); }); }); From fed2a92701c8b81bddc4caa48cf6cb0d58f87fd5 Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Mon, 13 Nov 2023 15:34:13 +0530 Subject: [PATCH 5/6] fix:Updated aliases for models Signed-off-by: Muskan Bararia --- README.md | 19 +------------------ index.js | 1 + 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 502299b..0b05374 100644 --- a/README.md +++ b/README.md @@ -50,23 +50,6 @@ Using Accord Project? Add a README badge to let everyone know: [![accord project [![accord project](https://img.shields.io/badge/powered%20by-accord%20project-19C6C8.svg)](https://www.accordproject.org/) ``` -### Concerto Decorate - -The `decorate` command allows you to add decorators and vocabularies to your local CTO file from a set of local DCS and Vocab Command Set files - -``` -concerto decorate - -apply the decorators and vocabs to the target models from given list of dcs files and vocab files - -Options: - --model The CTO file - --vocabulary The List of vocab files - --decorator The List of decorator command set files - --format output format (cto or json), default: cto - --output output directory path -``` - ## License Accord Project source code files are made available under the [Apache License, Version 2.0][apache]. @@ -88,4 +71,4 @@ Copyright 2018-2019 Clause, Inc. All trademarks are the property of their respec [developers]: https://github.com/accordproject/web-components/blob/master/DEVELOPERS.md [apache]: https://github.com/accordproject/web-components/blob/master/LICENSE -[creativecommons]: http://creativecommons.org/licenses/by/4.0/ \ No newline at end of file +[creativecommons]: http://creativecommons.org/licenses/by/4.0/ diff --git a/index.js b/index.js index 39f1f5c..c582285 100755 --- a/index.js +++ b/index.js @@ -412,6 +412,7 @@ require('yargs') yargs.demandOption('models', 'Please provide a model'); yargs.option('models', { describe: 'The file location of the source models', + alias: 'model', type: 'string', array:true, }); From a8673aba55c466310c6bf07979957866b8e53a0c Mon Sep 17 00:00:00 2001 From: Muskan Bararia Date: Mon, 13 Nov 2023 15:43:38 +0530 Subject: [PATCH 6/6] fix: updated jsdoc for options Signed-off-by: Muskan Bararia --- lib/commands.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/commands.js b/lib/commands.js index 03e64fe..5a1dcc8 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -636,6 +636,8 @@ class Commands { * @param {string[]} dcsFiles - the decorator files to be applied to model * @param {string[]} vocFiles - the vocab files to be applied to model * @param {object} options - optional parameters + * @param {string} options.format - format of output models (CTO or JSON) + * @param {string} options.output - the output directory where the decorated models are to be written * @returns {string} - Depending on the options, CTO string or JSON string */ static async decorate(modelFiles, dcsFiles,vocFiles,options) {