Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(core): Import / Export capabilities between CTO files and metamodel #298

Merged
merged 17 commits into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
54bc404
feature(core) Initial code to import/export metamodel from cto file
jeromesimeon Jul 14, 2021
318b734
fix(cli) new metamodel transform command for clarity
jeromesimeon Jul 15, 2021
e844034
fix(metamodel) Add validation when importing a metamodel + API change
jeromesimeon Jul 19, 2021
a13a723
feature(metamodel) Option to turn validation off (for the editor)
jeromesimeon Jul 19, 2021
8ba82af
feature(metamodel) Add support for default values
jeromesimeon Jul 19, 2021
c1331db
fix(core) Update TypeScript interface for MetaModel
jeromesimeon Jul 21, 2021
9d88ab9
feature(metamodel) Add ability to resolve type names + better CLI
jeromesimeon Jul 23, 2021
8445835
fix(core) Update the TypeScript interface
jeromesimeon Jul 26, 2021
a4c63c1
fix(metamodel) Hide fullyQualifiedName annotation for the form builder
jeromesimeon Jul 26, 2021
f3ccc4b
fix(metamodel) Distinguish between class declarations and enum declar…
jeromesimeon Jul 28, 2021
82d6796
feature(cli) Always include imported model for name resolution
jeromesimeon Jul 28, 2021
0c1229d
fix(metamodel) More explicit separation between enums and classes
jeromesimeon Jul 28, 2021
a724127
fix(metamodel) Some adjustments for the metamodel
jeromesimeon Jul 28, 2021
c002550
fix(metamodel) Add regular expressions for names back
jeromesimeon Aug 4, 2021
76f9837
feature(metamodel) Add support for validators (range and regex)
jeromesimeon Aug 4, 2021
609c93f
feature(metamodel) Add support for decorators
jeromesimeon Aug 4, 2021
a5ced22
fix(metamodel) Remove unused DecoratorIdentifier
jeromesimeon Aug 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/concerto-cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,51 @@ require('yargs')
Logger.error(err.message);
});
})
.command('import', 'import a cto string into its metamodel', (yargs) => {
yargs.demandOption(['input'], 'Please provide an input cto');
yargs.option('model', {
describe: 'array of concerto (cto) model files',
type: 'string',
array: true
});
yargs.option('input', {
describe: 'the cto model to import',
type: 'string'
});
yargs.option('resolve', {
describe: 'resolve names to fully qualified names',
type: 'boolean',
default: false
});
}, (argv) => {
return Commands.import(argv.input, argv.model, argv.resolve)
.then((result) => {
Logger.info(result);
})
.catch((err) => {
Logger.error(err.message);
});
})
.command('export', 'export a metamodel to cto syntax', (yargs) => {
yargs.demandOption(['input'], 'Please provide an input metamodel');
yargs.option('model', {
describe: 'array of concerto (cto) model files',
type: 'string',
array: true
});
yargs.option('input', {
describe: 'the metamodel to export',
type: 'string'
});
}, (argv) => {
return Commands.export(argv.input, argv.model)
.then((result) => {
Logger.info(result);
})
.catch((err) => {
Logger.error(err.message);
});
})
.option('verbose', {
alias: 'v',
default: false
Expand Down
42 changes: 40 additions & 2 deletions packages/concerto-cli/lib/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ModelLoader = require('@accordproject/concerto-core').ModelLoader;
const Factory = require('@accordproject/concerto-core').Factory;
const Serializer = require('@accordproject/concerto-core').Serializer;
const Concerto = require('@accordproject/concerto-core').Concerto;
const MetaModel = require('@accordproject/concerto-core').MetaModel;
const FileWriter = require('@accordproject/concerto-tools').FileWriter;
const CodeGen = require('@accordproject/concerto-tools').CodeGen;

Expand Down Expand Up @@ -165,8 +166,7 @@ class Commands {
parameters.fileWriter = new FileWriter(output);
modelManager.accept(visitor, parameters);
return `Compiled to ${target} in '${output}'.`;
}
else {
} else {
return 'Unrecognized target: ' + target;
}
}
Expand All @@ -184,6 +184,44 @@ class Commands {
modelManager.writeModelsToFileSystem(output);
return `Loaded external models in '${output}'.`;
}

/**
* Import a CTO string to its metamodel
*
* @param {string} input - CTO
* @param {string[]} [ctoFiles] - the CTO files used for import resolution
* @param {boolean} resolve - whether to resolve the names
* @param {string} the metamodel
*/
static async import(input, ctoFiles = [], resolve) {
// Add input to ctoFiles for convenience
if (!ctoFiles.includes(input)) {
ctoFiles.push(input);
}
const inputString = fs.readFileSync(input, 'utf8');
let result;
if (resolve) {
const modelManager = await ModelLoader.loadModelManager(ctoFiles);
result = MetaModel.ctoToMetaModelAndResolve(modelManager, inputString);
} else {
result = MetaModel.ctoToMetaModel(inputString);
}
return JSON.stringify(result);
}

/**
* Export a metamodel to a CTO string
*
* @param {string} input metamodel
* @param {string[]} ctoFiles - the CTO files used for import resolution
* @param {string} transformed (meta)model
*/
static async export(input, ctoFiles) {
const inputString = fs.readFileSync(input, 'utf8');
const json = JSON.parse(inputString);
const result = MetaModel.ctoFromMetaModel(json);
return result;
}
}

module.exports = Commands;
24 changes: 24 additions & 0 deletions packages/concerto-cli/test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,28 @@ describe('cicero-cli', () => {
dir.cleanup();
});
});

describe('#import', async () => {
it('should transform cto to metamodel', async () => {
const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'models/contract.json')));
const result = JSON.parse(await Commands.import(path.resolve(__dirname, 'models/contract.cto')));
result.should.deep.equal(expected);
});

it('should transform cto to metamodel and resolve names', async () => {
const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'models/contractResolved.json')));
const contractFile = path.resolve(__dirname, 'models/contract.cto');
const result = JSON.parse(await Commands.import(contractFile, [contractFile], true));
result.should.deep.equal(expected);
});
});

describe('#export', async () => {
it('should transform a metamodel to cto', async () => {
const expected = fs.readFileSync(path.resolve(__dirname, 'models/contract2.cto'), 'utf-8');
const metamodel = path.resolve(__dirname, 'models/contract.json');
const result = await Commands.export(metamodel);
result.should.equal(expected);
});
});
});
86 changes: 86 additions & 0 deletions packages/concerto-cli/test/models/contract.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"$class": "concerto.metamodel.ModelFile",
"namespace": "org.accordproject.cicero.contract",
"imports": [],
"enumDeclarations": [],
"classDeclarations": [
{
"$class": "concerto.metamodel.AssetDeclaration",
"name": "AccordContractState",
"isAbstract": false,
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "stateId"
},
"fields": [
{
"name": "stateId",
"isArray": false,
"isOptional": false,
"$class": "concerto.metamodel.StringFieldDeclaration"
}
]
},
{
"$class": "concerto.metamodel.ParticipantDeclaration",
"name": "AccordParty",
"isAbstract": false,
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "partyId"
},
"fields": [
{
"name": "partyId",
"isArray": false,
"isOptional": false,
"$class": "concerto.metamodel.StringFieldDeclaration"
}
]
},
{
"$class": "concerto.metamodel.AssetDeclaration",
"name": "AccordContract",
"isAbstract": true,
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "contractId"
},
"fields": [
{
"name": "contractId",
"isArray": false,
"isOptional": false,
"$class": "concerto.metamodel.StringFieldDeclaration"
},
{
"$class": "concerto.metamodel.RelationshipDeclaration",
"type": {
"$class": "concerto.metamodel.TypeIdentifier",
"name": "AccordParty"
},
"name": "parties",
"isArray": true,
"isOptional": true
}
]
},
{
"$class": "concerto.metamodel.AssetDeclaration",
"name": "AccordClause",
"isAbstract": true,
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "clauseId"
},
"fields": [
{
"name": "clauseId",
"isArray": false,
"isOptional": false,
"$class": "concerto.metamodel.StringFieldDeclaration"
}
]
}
]
}
18 changes: 18 additions & 0 deletions packages/concerto-cli/test/models/contract2.cto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace org.accordproject.cicero.contract

asset AccordContractState identified by stateId {
o String stateId
}

participant AccordParty identified by partyId {
o String partyId
}

abstract asset AccordContract identified by contractId {
o String contractId
--> AccordParty[] parties optional
}

abstract asset AccordClause identified by clauseId {
o String clauseId
}
87 changes: 87 additions & 0 deletions packages/concerto-cli/test/models/contractResolved.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"$class": "concerto.metamodel.ModelFile",
"namespace": "org.accordproject.cicero.contract",
"imports": [],
"enumDeclarations": [],
"classDeclarations": [
{
"$class": "concerto.metamodel.AssetDeclaration",
"isAbstract": false,
"name": "AccordContractState",
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "stateId"
},
"fields": [
{
"$class": "concerto.metamodel.StringFieldDeclaration",
"name": "stateId",
"isArray": false,
"isOptional": false
}
]
},
{
"$class": "concerto.metamodel.ParticipantDeclaration",
"isAbstract": false,
"name": "AccordParty",
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "partyId"
},
"fields": [
{
"$class": "concerto.metamodel.StringFieldDeclaration",
"name": "partyId",
"isArray": false,
"isOptional": false
}
]
},
{
"$class": "concerto.metamodel.AssetDeclaration",
"isAbstract": true,
"name": "AccordContract",
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "contractId"
},
"fields": [
{
"$class": "concerto.metamodel.StringFieldDeclaration",
"name": "contractId",
"isArray": false,
"isOptional": false
},
{
"$class": "concerto.metamodel.RelationshipDeclaration",
"type": {
"$class": "concerto.metamodel.TypeIdentifier",
"name": "AccordParty",
"fullyQualifiedName": "org.accordproject.cicero.contract.AccordParty"
},
"name": "parties",
"isArray": true,
"isOptional": true
}
]
},
{
"$class": "concerto.metamodel.AssetDeclaration",
"isAbstract": true,
"name": "AccordClause",
"identified": {
"$class": "concerto.metamodel.IdentifiedBy",
"name": "clauseId"
},
"fields": [
{
"$class": "concerto.metamodel.StringFieldDeclaration",
"name": "clauseId",
"isArray": false,
"isOptional": false
}
]
}
]
}
25 changes: 25 additions & 0 deletions packages/concerto-core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,30 @@ class ModelFileDownloader {
+ Promise downloadExternalDependencies(ModelFile[],Object)
+ Promise runJob(Object,Object)
}
+ void createMetaModelManager()
+ object validateMetaModel()
+ object createNameTable()
+ string resolveName()
+ object resolveTypeNames()
+ object enumFieldToMetaModel()
+ object decoratorArgToMetaModel()
+ object decoratorToMetaModel()
+ object decoratorsToMetaModel()
+ object fieldToMetaModel()
+ object relationshipToMetaModel()
+ object enumDeclToMetaModel()
+ object classDeclToMetaModel()
+ object declToMetaModel()
+ object modelToMetaModel()
+ object modelFileToMetaModel()
+ string decoratorArgFromMetaModel()
+ string decoratorFromMetaModel()
+ string decoratorsFromMetaModel()
+ string fieldFromMetaModel()
+ string declFromMetaModel()
+ string ctoFromMetaModel()
+ object ctoToMetaModel()
+ object ctoToMetaModelAndResolve()
class ModelFile {
+ void constructor(ModelManager,string,string) throws IllegalModelException
+ Boolean isSystemModelFile()
Expand All @@ -133,6 +157,7 @@ class ModelFile {
+ ClassDeclaration[] getAllDeclarations()
+ string getDefinitions()
+ string getConcertoVersion()
+ void isCompatibleVersion()
+ boolean hasInstance(object)
}
class ParticipantDeclaration extends IdentifiedDeclaration {
Expand Down
3 changes: 3 additions & 0 deletions packages/concerto-core/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#

Version 1.0.5 {16fb2d5684ec917532a19428c74f1ebf} 2021-07-13
- Add support for Concerto metamodel with import/export to CTO

Version 1.0.3 {1fe469fe1a79af5d5a4f5ec7dee6b7d4} 2021-06-25
- Aligns JSDoc and the TypeScript interface

Expand Down
3 changes: 3 additions & 0 deletions packages/concerto-core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,8 @@ module.exports.TypedStack = require('./lib/serializer/typedstack');
// Concerto
module.exports.Concerto = require('./lib/concerto');

// MetaModel
module.exports.MetaModel = require('./lib/introspect/metamodel');

// Version
module.exports.version = require('./package.json');
Loading