diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 7a070e50e..8470be80c 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -55,6 +55,7 @@ class Concerto { } + object setCurrentTime() class DecoratorManager { + + ModelManager validate(decoratorCommandSet,ModelFile[]) throws Error + ModelManager decorateModels(ModelManager,decoratorCommandSet,object?,boolean?,boolean?) + void validateCommand(ModelManager,command) + Boolean falsyOrEqual(string||,string[]) diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index d00828ad2..c219a9205 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,8 +24,9 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # -Version 3.13.0 {a7060663ad5bb322ec4ee760baa7ab1a} 2023-10-01 +Version 3.13.0 {125b7f97f8740628b2629b2793384cc7} 2023-10-03 - Update DecoratorManager to support multiple value compare +- Create DecoratorManager.validate method to validate structure of decorator command set Version 3.12.4 {7738d5490ea8438677e1d21d704bb5aa} 2023-08-31 - Adds validate and validateCommands options to DecoratorManager.decorateModels diff --git a/packages/concerto-core/lib/decoratormanager.js b/packages/concerto-core/lib/decoratormanager.js index 1136609dc..718b16d8a 100644 --- a/packages/concerto-core/lib/decoratormanager.js +++ b/packages/concerto-core/lib/decoratormanager.js @@ -19,6 +19,15 @@ const Serializer = require('./serializer'); const Factory = require('./factory'); const ModelUtil = require('./modelutil'); +// Types needed for TypeScript generation. +/* eslint-disable no-unused-vars */ +/* istanbul ignore next */ +if (global === undefined) { + const ModelFile = require('./introspect/modelfile'); +} +/* eslint-enable no-unused-vars */ + + const DCS_MODEL = `concerto version "^3.0.0" namespace org.accordproject.decoratorcommands@0.3.0 @@ -105,6 +114,39 @@ function isUnversionedNamespaceEqual(modelFile, unversionedNamespace) { * @memberof module:concerto-core */ class DecoratorManager { + + /** + * Structural validation of the decoratorCommandSet against the + * Decorator Command Set model. Note that this only checks the + * structural integrity of the command set, it cannot check + * whether the commands are valid with respect to a model manager. + * Use the options.validateCommands option with decorateModels + * method to perform semantic validation. + * @param {*} decoratorCommandSet the DecoratorCommandSet object + * @param {ModelFile[]} [modelFiles] an optional array of model + * files that are added to the validation model manager returned + * @returns {ModelManager} the model manager created for validation + * @throws {Error} throws an error if the decoratorCommandSet is invalid + */ + static validate(decoratorCommandSet, modelFiles) { + const validationModelManager = new ModelManager({ + strict: true, + metamodelValidation: true, + addMetamodel: true, + }); + if(modelFiles) { + validationModelManager.addModelFiles(modelFiles); + } + validationModelManager.addCTOModel( + DCS_MODEL, + 'decoratorcommands@0.3.0.cto' + ); + const factory = new Factory(validationModelManager); + const serializer = new Serializer(factory, validationModelManager); + serializer.fromJSON(decoratorCommandSet); + return validationModelManager; + } + /** * Applies all the decorator commands from the DecoratorCommandSet * to the ModelManager. @@ -119,19 +161,7 @@ class DecoratorManager { */ static decorateModels(modelManager, decoratorCommandSet, options) { if (options?.validate) { - const validationModelManager = new ModelManager({ - strict: true, - metamodelValidation: true, - addMetamodel: true, - }); - validationModelManager.addModelFiles(modelManager.getModelFiles()); - validationModelManager.addCTOModel( - DCS_MODEL, - 'decoratorcommands@0.2.0.cto' - ); - 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( diff --git a/packages/concerto-core/test/decoratormanager.js b/packages/concerto-core/test/decoratormanager.js index ac31e6c41..a635d10cb 100644 --- a/packages/concerto-core/test/decoratormanager.js +++ b/packages/concerto-core/test/decoratormanager.js @@ -57,6 +57,36 @@ describe('DecoratorManager', () => { }); }); + describe('#validate', function() { + it('should support syntax validation', async function() { + const dcs = fs.readFileSync('./test/data/decoratorcommands/web.json', 'utf-8'); + const validationModelManager = DecoratorManager.validate( JSON.parse(dcs)); + validationModelManager.should.not.be.null; + }); + + it('should support syntax validation with model files', async function() { + 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/web.json', 'utf-8'); + const validationModelManager = DecoratorManager.validate(JSON.parse(dcs), testModelManager.getModelFiles()); + validationModelManager.should.not.be.null; + validationModelManager.getType('test@1.0.0.Person').should.not.be.null; + }); + + it('should fail syntax validation', async function() { + (() => { + DecoratorManager.validate( { $class: 'invalid' }); + }).should.throw(/Namespace is not defined for type/); + }); + + it('should fail syntax validation', async function() { + (() => { + DecoratorManager.validate( { invalid: true }); + }).should.throw(/Invalid JSON data/); + }); + }); + describe('#decorateModels', function() { it('should support no validation', async function() { const testModelManager = new ModelManager({strict:true}); diff --git a/packages/concerto-core/types/lib/decoratormanager.d.ts b/packages/concerto-core/types/lib/decoratormanager.d.ts index 480e9c816..183409d8a 100644 --- a/packages/concerto-core/types/lib/decoratormanager.d.ts +++ b/packages/concerto-core/types/lib/decoratormanager.d.ts @@ -5,6 +5,20 @@ export = DecoratorManager; * @memberof module:concerto-core */ declare class DecoratorManager { + /** + * Structural validation of the decoratorCommandSet against the + * Decorator Command Set model. Note that this only checks the + * structural integrity of the command set, it cannot check + * whether the commands are valid with respect to a model manager. + * Use the options.validateCommands option with decorateModels + * method to perform semantic validation. + * @param {*} decoratorCommandSet the DecoratorCommandSet object + * @param {ModelFile[]} [modelFiles] an optional array of model + * files that are added to the validation model manager returned + * @returns {ModelManager} the model manager created for validation + * @throws {Error} throws an error if the decoratorCommandSet is invalid + */ + static validate(decoratorCommandSet: any, modelFiles?: ModelFile[]): ModelManager; /** * Applies all the decorator commands from the DecoratorCommandSet * to the ModelManager. @@ -61,4 +75,5 @@ declare class DecoratorManager { */ static executePropertyCommand(property: any, command: any): void; } +import ModelFile = require("./introspect/modelfile"); import ModelManager = require("./modelmanager");