Skip to content

Commit

Permalink
validated decorators
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Selman <[email protected]>
  • Loading branch information
dselman committed Oct 4, 2024
1 parent 55704f6 commit 0f02cf8
Show file tree
Hide file tree
Showing 25 changed files with 375 additions and 299 deletions.
3 changes: 2 additions & 1 deletion packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class BaseModelManager {
+ boolean derivesFrom(string,string)
+ object resolveMetaModel(object)
+ void fromAst(ast)
+ void getAst(boolean?)
+ void getAst(boolean?,boolean?)
+ BaseModelManager filter(FilterFunction)
}
class Concerto {
Expand Down Expand Up @@ -70,6 +70,7 @@ class DecoratorManager {
}
+ string[] intersect()
+ boolean isUnversionedNamespaceEqual()
+ object getDecoratorModel()
class Factory {
+ string newId()
+ void constructor(ModelManager)
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 3.19.2 {ab559ed3795420fd881b8a9a5a42f9b1} 2024-10-02
Version 3.19.2 {56bc38dc7305eee0a06f08a8cf639910} 2024-10-02
- validateDecorators option added to ModelManager
- update DecoratorManager to support validated decorators

Expand Down
29 changes: 24 additions & 5 deletions packages/concerto-core/lib/basemodelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const ModelUtil = require('./modelutil');
const Serializer = require('./serializer');
const TypeNotFoundException = require('./typenotfoundexception');
const { getRootModel } = require('./rootmodel');
const { getDecoratorModel } = require('./decoratormodel');
const MetamodelException = require('./metamodelexception');

// Types needed for TypeScript generation.
Expand Down Expand Up @@ -63,6 +64,10 @@ const DEFAULT_DECORATOR_VALIDATION = {
invalidDecorator: undefined, // 'error' | 'warn' ...
};

// these namespaces are internal and excluded by default by getModelFiles
// and ignored by fromAst
const EXCLUDE_NS = ['[email protected]', 'concerto', '[email protected]'];

/**
* Manages the Concerto model files.
*
Expand Down Expand Up @@ -102,6 +107,7 @@ class BaseModelManager {
this.decoratorFactories = [];
this.strict = !!options?.strict;
this.options = options;
this.addDecoratorModel();
this.addRootModel();
this.decoratorValidation = options?.decoratorValidation ? options?.decoratorValidation : DEFAULT_DECORATOR_VALIDATION;

Expand Down Expand Up @@ -166,6 +172,16 @@ class BaseModelManager {
}
}

/**
* Adds decorator types
* @private
*/
addDecoratorModel() {
const {decoratorModelAst, decoratorModelCto, decoratorModelFile} = getDecoratorModel();
const m = new ModelFile(this, decoratorModelAst, decoratorModelCto, decoratorModelFile);
this.addModelFile(m, decoratorModelCto, decoratorModelFile, true);
}

/**
* Visitor design pattern
* @param {Object} visitor - the visitor
Expand Down Expand Up @@ -501,7 +517,7 @@ class BaseModelManager {

for (let n = 0; n < keys.length; n++) {
const ns = keys[n];
if(includeConcertoNamespace || (ns !== '[email protected]' && ns !== 'concerto')) {
if(includeConcertoNamespace || (!EXCLUDE_NS.includes(ns))) {
result.push(this.modelFiles[ns]);
}
}
Expand Down Expand Up @@ -580,6 +596,7 @@ class BaseModelManager {
*/
clearModelFiles() {
this.modelFiles = {};
this.addDecoratorModel();
this.addRootModel();
}

Expand Down Expand Up @@ -770,7 +787,7 @@ class BaseModelManager {
* @return {object} the resolved metamodel
*/
resolveMetaModel(metaModel) {
const priorModels = this.getAst();
const priorModels = this.getAst(false, true);
return MetaModelUtil.resolveLocalNames(priorModels, metaModel);
}

Expand All @@ -781,25 +798,27 @@ class BaseModelManager {
fromAst(ast) {
this.clearModelFiles();
ast.models.forEach( model => {
if(model.namespace !== '[email protected]') { // excludes the Concerto namespace, already added
if(!EXCLUDE_NS.includes(model.namespace)) { // excludes the internal namespaces, already added
const modelFile = new ModelFile( this, model );
this.addModelFile( modelFile, null, null, true );
}
});
console.log(Object.keys(this.modelFiles));

Check warning on line 806 in packages/concerto-core/lib/basemodelmanager.js

View workflow job for this annotation

GitHub Actions / Unit Tests (18.x, macos-latest)

Unexpected console statement

Check warning on line 806 in packages/concerto-core/lib/basemodelmanager.js

View workflow job for this annotation

GitHub Actions / Unit Tests (18.x, ubuntu-latest)

Unexpected console statement

Check warning on line 806 in packages/concerto-core/lib/basemodelmanager.js

View workflow job for this annotation

GitHub Actions / Unit Tests (20.x, macos-latest)

Unexpected console statement

Check warning on line 806 in packages/concerto-core/lib/basemodelmanager.js

View workflow job for this annotation

GitHub Actions / Unit Tests (20.x, ubuntu-latest)

Unexpected console statement
this.validateModelFiles();
}

/**
* Get the full ast (metamodel instances) for a modelmanager
* @param {boolean} [resolve] - whether to resolve names
* @param {boolean} [includeConcertoNamespaces] - whether to include the concerto namespaces
* @returns {*} the metamodel
*/
getAst(resolve) {
getAst(resolve,includeConcertoNamespaces) {
const result = {
$class: `${MetaModelNamespace}.Models`,
models: [],
};
const modelFiles = this.getModelFiles(true); // includes the Concerto namespace
const modelFiles = this.getModelFiles(includeConcertoNamespaces);
modelFiles.forEach((thisModelFile) => {
let metaModel = thisModelFile.getAst();
if (resolve) {
Expand Down
18 changes: 7 additions & 11 deletions packages/concerto-core/lib/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,24 +374,20 @@ class DecoratorManager {

this.migrateAndValidate(modelManager, decoratorCommandSet, options?.migrate, options?.validate, options?.validateCommands);

// we create synthetic imports for all decorator declarations
const decoratorImports = decoratorCommandSet.commands.map(command => {
return {
$class: `${MetaModelNamespace}.ImportType`,
name: command.decorator.name,
namespace: command.decorator.namespace ? command.decorator.namespace : options.defaultNamespace
namespace: command.decorator.namespace ? command.decorator.namespace : options?.defaultNamespace
};
}).filter(i => i.namespace);
decoratorImports.push({
$class: `${MetaModelNamespace}.ImportType`,
name: 'Decorator',
namespace: '[email protected]'
});

const { namespaceCommandsMap, declarationCommandsMap, propertyCommandsMap, mapElementCommandsMap, typeCommandsMap } = this.getDecoratorMaps(decoratorCommandSet);
const ast = modelManager.getAst(true);
const ast = modelManager.getAst(true, true);
const decoratedAst = JSON.parse(JSON.stringify(ast));
decoratedAst.models.forEach((model) => {
model.imports = decoratorImports;
// add the imports for all decorators, in case they get added below
model.imports = model.imports ? model.imports.concat(decoratorImports) : decoratorImports;
model.declarations.forEach((decl) => {
const declarationDecoratorCommandSets = [];
const { name: declarationName, $class: $classForDeclaration } = decl;
Expand Down Expand Up @@ -467,7 +463,7 @@ class DecoratorManager {
locale:'en',
...options
};
const sourceAst = modelManager.getAst(true);
const sourceAst = modelManager.getAst(true, true);
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst, DecoratorExtractor.Action.EXTRACT_ALL);
const collectionResp = decoratorExtrator.extract();
return {
Expand All @@ -490,7 +486,7 @@ class DecoratorManager {
locale:'en',
...options
};
const sourceAst = modelManager.getAst(true);
const sourceAst = modelManager.getAst(true, true);
const decoratorExtrator = new DecoratorExtractor(options.removeDecoratorsFromModel, options.locale, DCS_VERSION, sourceAst, DecoratorExtractor.Action.EXTRACT_VOCAB);
const collectionResp = decoratorExtrator.extract();
return {
Expand Down
35 changes: 35 additions & 0 deletions packages/concerto-core/lib/decoratormodel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/** @type unknown */
const decoratorModelAst = require('./decoratormodel.json');

/**
* Gets the decorator 'concerto.decorator' model
* @returns {object} decoratorModelFile, decoratorModelCto and decoratorModelAst
*/
function getDecoratorModel() {
const decoratorModelFile = 'concerto_decorator_1.0.0.cto';
const decoratorModelCto = `namespace [email protected]
abstract concept Decorator {}
concept DotNetNamespace extends Decorator {
o String namespace
}`;
const ast = JSON.parse(JSON.stringify(decoratorModelAst));
return { decoratorModelFile, decoratorModelCto, decoratorModelAst: ast };
}

module.exports = { getDecoratorModel };
31 changes: 31 additions & 0 deletions packages/concerto-core/lib/decoratormodel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$class": "[email protected]",
"decorators": [],
"namespace": "[email protected]",
"imports": [],
"declarations": [
{
"$class": "[email protected]",
"name": "Decorator",
"isAbstract": true,
"properties": []
},
{
"$class": "[email protected]",
"name": "DotNetNamespace",
"superType": {
"$class": "[email protected]",
"name": "Decorator"
},
"isAbstract": false,
"properties": [
{
"$class": "[email protected]",
"name": "namespace",
"isArray": false,
"isOptional": false
}
]
}
]
}
8 changes: 3 additions & 5 deletions packages/concerto-core/lib/rootmodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,16 @@ function getRootModel(versioned) {
const ns = versioned ? '[email protected]' : 'concerto';
const rootModelCto = `@DotNetNamespace("AccordProject.Concerto")
namespace ${ns}
import [email protected]
abstract concept Concept {}
abstract concept Asset identified {}
abstract concept Participant identified {}
abstract concept Transaction {}
abstract concept Event {}
abstract concept Decorator {}
concept DotNetNamespace extends Decorator {
o String namespace
}`;
`;
const ast = JSON.parse(JSON.stringify(rootModelAst));
ast.namespace = ns;
return { rootModelFile, rootModelCto, rootModelAst: ast };
}

module.exports = { getRootModel };
module.exports = { getRootModel };
31 changes: 7 additions & 24 deletions packages/concerto-core/lib/rootmodel.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
}
],
"namespace": "[email protected]",
"imports": [],
"imports": [
{
"$class": "[email protected]",
"name": "DotNetNamespace",
"namespace": "[email protected]"
}
],
"declarations": [
{
"$class": "[email protected]",
Expand Down Expand Up @@ -50,29 +56,6 @@
"name": "Event",
"isAbstract": true,
"properties": []
},
{
"$class": "[email protected]",
"name": "Decorator",
"isAbstract": true,
"properties": []
},
{
"$class": "[email protected]",
"name": "DotNetNamespace",
"superType": {
"$class": "[email protected]",
"name": "Decorator"
},
"isAbstract": false,
"properties": [
{
"$class": "[email protected]",
"name": "namespace",
"isArray": false,
"isOptional": false
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
namespace [email protected]

import [email protected]

@Declaration()
@NamespaceDeclaration()
@NamespaceDeprecated()
@Namespace()
scalar SSN extends String

@NamespaceDeprecated()
@Namespace()
concept Editable extends Decorator {
}

@NamespaceDeprecated()
@Namespace()
concept Custom extends Decorator {
}

@Editable
@NamespaceDeprecated()
@Namespace()
Expand Down
Loading

0 comments on commit 0f02cf8

Please sign in to comment.