Skip to content

Commit

Permalink
fix(metamodel) Add validation when importing a metamodel + API change
Browse files Browse the repository at this point in the history
Signed-off-by: jeromesimeon <[email protected]>
  • Loading branch information
jeromesimeon committed Jul 19, 2021
1 parent 2449cb2 commit 1044f77
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 21 deletions.
6 changes: 4 additions & 2 deletions packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,17 @@ class ModelFileDownloader {
+ Promise downloadExternalDependencies(ModelFile[],Object)
+ Promise runJob(Object,Object)
}
+ void createMetaModelManager()
+ object fieldToMetaModel()
+ object relationshipToMetaModel()
+ object enumPropertyToMetaModel()
+ object declToMetaModel()
+ object modelToMetaModel()
+ object modelFileToMetaModel()
+ string fieldFromMetaModel()
+ string declFromMetaModel()
+ string modelFromMetaModel()
+ string ctoFromMetaModel()
+ object ctoToMetaModel()
class ModelFile {
+ void constructor(ModelManager,string,string) throws IllegalModelException
+ Boolean isSystemModelFile()
Expand All @@ -142,7 +145,6 @@ class ModelFile {
+ string getDefinitions()
+ string getConcertoVersion()
+ void isCompatibleVersion()
+ object toMetaModel()
+ boolean hasInstance(object)
}
class ParticipantDeclaration extends IdentifiedDeclaration {
Expand Down
2 changes: 1 addition & 1 deletion packages/concerto-core/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#

Version 1.0.5 {febf7c3ec5ac5ec2f7f9fa88bd823555} 2021-07-13
Version 1.0.5 {5af6b5621ee3ca9c92d5c665b641cc62} 2021-07-13
- Add support for Concerto metamodel with import/export to CTO

Version 1.0.3 {1fe469fe1a79af5d5a4f5ec7dee6b7d4} 2021-06-25
Expand Down
264 changes: 259 additions & 5 deletions packages/concerto-core/lib/introspect/metamodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,232 @@

'use strict';

const parser = require('./parser');
const ModelManager = require('../modelmanager');
const Factory = require('../factory');
const Serializer = require('../serializer');

const metaModelCto = `/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace concerto.metamodel
/**
* The metadmodel for Concerto files
*/
abstract concept DecoratorLiteral {
}
concept DecoratorString extends DecoratorLiteral {
o String value
}
concept DecoratorNumber extends DecoratorLiteral {
o Double value
}
concept DecoratorBoolean extends DecoratorLiteral {
o Boolean value
}
concept TypeIdentifier {
@FormEditor("selectOptions", "types")
o String name default="Concept"
}
concept DecoratorIdentifier extends DecoratorLiteral {
o TypeIdentifier identifier
o Boolean isArray default=false
}
concept Decorator {
o String name
o DecoratorLiteral[] arguments optional
}
concept Identified {
}
concept IdentifiedBy extends Identified {
o String name
}
@FormEditor("defaultSubclass","concerto.metamodel.ConceptDeclaration")
abstract concept ClassDeclaration {
@FormEditor("hide", true)
o Decorator[] decorators optional
o Boolean isAbstract default=false
// TODO use regex /^(?!null|true|false)(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*/u
@FormEditor("title", "Name")
o String name default="ClassName" // regex=/^(?!null|true|false)(\\w|\\d|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\w|\\d|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\S|\\u200C|\\u200D)*$/
o Identified identified optional
@FormEditor("title", "Super Type")
o TypeIdentifier superType optional
o FieldDeclaration[] fields
}
concept AssetDeclaration extends ClassDeclaration {
}
concept ParticipantDeclaration extends ClassDeclaration {
}
concept TransactionDeclaration extends ClassDeclaration {
}
concept EventDeclaration extends ClassDeclaration {
}
concept ConceptDeclaration extends ClassDeclaration {
}
concept EnumDeclaration extends ClassDeclaration {
}
concept StringDefault {
o String value
}
concept BooleanDefault {
o Boolean value
}
concept IntegerDefault {
o Integer value
}
concept LongDefault {
o Long value
}
concept DoubleDefault {
o Double value
}
@FormEditor("defaultSubclass","concerto.metamodel.StringFieldDeclaration")
abstract concept FieldDeclaration {
// TODO Allow regex modifiers e.g. //ui
// regex /^(?!null|true|false)(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\pLm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*/u
// This regex is an approximation of what the parser accepts without using unicode character classes
o String name default="fieldName" // regex=/^(?!null|true|false)(\\w|\\d|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\w|\\d|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\S|\\u200C|\\u200D)*$/
@FormEditor("title", "Is Array?")
o Boolean isArray default=false
@FormEditor("title", "Is Optional?")
o Boolean isOptional default=false
@FormEditor("hide", true)
o Decorator[] decorators optional
}
concept ObjectFieldDeclaration extends FieldDeclaration {
@FormEditor("hide", true)
o StringDefault defaultValue optional
@FormEditor("title", "Type Name", "selectOptions", "types")
o TypeIdentifier type
}
concept EnumFieldDeclaration extends FieldDeclaration {
}
concept BooleanFieldDeclaration extends FieldDeclaration {
@FormEditor("hide", true)
o BooleanDefault defaultValue optional
}
concept DateTimeFieldDeclaration extends FieldDeclaration {
}
concept StringFieldDeclaration extends FieldDeclaration {
@FormEditor("hide", true)
o StringDefault defaultValue optional
@FormEditor("hide", true)
o StringRegexValidator validator optional
}
concept StringRegexValidator {
o String regex
}
concept DoubleDomainValidator {
o Double lower optional
o Double upper optional
}
concept IntegerDomainValidator {
o Integer lower optional
o Integer upper optional
}
concept LongDomainValidator {
o Long lower optional
o Long upper optional
}
concept DoubleFieldDeclaration extends FieldDeclaration {
o DoubleDefault defaultValue optional
o DoubleDomainValidator validator optional
}
concept IntegerFieldDeclaration extends FieldDeclaration {
@FormEditor("hide", true)
o IntegerDefault defaultValue optional
@FormEditor("hide", true)
o IntegerDomainValidator validator optional
}
concept LongFieldDeclaration extends FieldDeclaration {
@FormEditor("hide", true)
o LongDefault defaultValue optional
@FormEditor("hide", true)
o LongDomainValidator validator optional
}
concept RelationshipDeclaration extends FieldDeclaration {
@FormEditor("title", "Type Name", "selectOptions", "types")
o TypeIdentifier type
}
abstract concept Import {
o String namespace
o String uri optional
}
concept ImportAll extends Import {
}
concept ImportType extends Import {
o TypeIdentifier identifier
}
concept ModelFile {
o String namespace default="my.namespace"
@FormEditor("hide", true)
o Import[] imports optional
@FormEditor("title", "Classes")
o ClassDeclaration[] declarations optional
}
`;

/**
* Create a metamodel manager (for validation against the metamodel)
* @return {*} the metamodel manager
*/
function createMetaModelManager() {
const metaModelManager = new ModelManager();
metaModelManager.addModelFile(metaModelCto, 'concerto.metamodel');
return metaModelManager;
}

/**
* Create metamodel for a field
* @param {object} ast - the AST for the field
Expand Down Expand Up @@ -200,7 +426,7 @@ function declToMetaModel(ast) {
}

/**
* Export metamodel
* Export metamodel from an AST
* @param {object} ast - the AST for the model
* @return {object} the metamodel for this model
*/
Expand Down Expand Up @@ -247,6 +473,15 @@ function modelToMetaModel(ast) {
return metamodel;
}

/**
* Export metamodel from a model file
* @param {object} modelFile - the AST for the model
* @return {object} the metamodel for this model
*/
function modelFileToMetaModel(modelFile) {
return modelToMetaModel(modelFile.ast);
}

/**
* Create a field string from a metamodel
* @param {object} mm - the metamodel
Expand Down Expand Up @@ -348,10 +583,17 @@ function declFromMetaModel(mm) {

/**
* Create a model string from a metamodel
* @param {object} mm - the metamodel
* @param {object} metaModel - the metamodel
* @return {string} the string for that model
*/
function modelFromMetaModel(mm) {
function ctoFromMetaModel(metaModel) {
const metaModelManager = createMetaModelManager();
const factory = new Factory(metaModelManager);
const serializer = new Serializer(factory, metaModelManager);
// First validate the metaModel
const object = serializer.fromJSON(metaModel);
const mm = serializer.toJSON(object);

let result = '';
result += `namespace ${mm.namespace}`;
if (mm.imports && mm.imports.length > 0) {
Expand All @@ -375,7 +617,19 @@ function modelFromMetaModel(mm) {
return result;
}

/**
* Export metamodel from a model string
* @param {object} model - the string for the model
* @return {object} the metamodel for this model
*/
function ctoToMetaModel(model) {
const ast = parser.parse(model);
return modelToMetaModel(ast);
}

module.exports = {
modelToMetaModel,
modelFromMetaModel,
metaModelCto,
modelFileToMetaModel,
ctoToMetaModel,
ctoFromMetaModel,
};
9 changes: 0 additions & 9 deletions packages/concerto-core/lib/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
const packageJson = require('../../package.json');
const semver = require('semver');
const parser = require('./parser');
const MetaModel = require('./metamodel');
const AssetDeclaration = require('./assetdeclaration');
const EnumDeclaration = require('./enumdeclaration');
const ConceptDeclaration = require('./conceptdeclaration');
Expand Down Expand Up @@ -573,14 +572,6 @@ class ModelFile {
}
}

/**
* Export metamodel
* @return {object} the metamodel for this model file
*/
toMetaModel() {
return MetaModel.modelToMetaModel(this.ast);
}

/**
* Populate from an AST
* @param {object} ast - the AST obtained from the parser
Expand Down
12 changes: 8 additions & 4 deletions packages/concerto-core/test/introspect/metamodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,20 @@ describe('MetaModel', () => {
const personMetaModel = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../data/model/person.json'), 'utf8'));

describe('#toMetaModel', () => {
it('should convert a CTO model to its metamodel', () => {
const mm1 = MetaModel.ctoToMetaModel(personModel);
mm1.should.deep.equal(personMetaModel);
});

it('should convert CTO file to its metamodel', () => {
it('should convert a ModelFile to its metamodel', () => {
const modelManager1 = new ModelManager();
const mf1 = new ModelFile(modelManager1, personModel);
const mm1 = mf1.toMetaModel();
const mm1 = MetaModel.modelFileToMetaModel(mf1);
mm1.should.deep.equal(personMetaModel);
const model2 = MetaModel.modelFromMetaModel(mm1);
const model2 = MetaModel.ctoFromMetaModel(mm1);
const modelManager2 = new ModelManager();
const mf2 = new ModelFile(modelManager2, model2);
const mm2 = mf2.toMetaModel();
const mm2 = MetaModel.modelFileToMetaModel(mf2);
mm2.should.deep.equal(personMetaModel);
});
});
Expand Down

0 comments on commit 1044f77

Please sign in to comment.