Skip to content

Commit 999fa14

Browse files
committed
refactor(core) Add utility for loading a model manager
Signed-off-by: Jerome Simeon <[email protected]>
1 parent 8eec864 commit 999fa14

File tree

7 files changed

+219
-73
lines changed

7 files changed

+219
-73
lines changed

packages/concerto-cli/lib/commands.js

Lines changed: 6 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
const fs = require('fs');
1818
const mkdirp = require('mkdirp');
1919

20-
const { ModelManager, Factory, Serializer } = require('@accordproject/concerto-core');
21-
const ModelFile = require('@accordproject/concerto-core').ModelFile;
22-
const DefaultModelFileLoader = require('@accordproject/concerto-core').DefaultModelFileLoader;
20+
const ModelLoader = require('@accordproject/concerto-core').ModelLoader;
21+
const Factory = require('@accordproject/concerto-core').Factory;
22+
const Serializer = require('@accordproject/concerto-core').Serializer;
2323
const FileWriter = require('@accordproject/concerto-tools').FileWriter;
2424
const CodeGen = require('@accordproject/concerto-tools').CodeGen;
2525

@@ -30,77 +30,12 @@ const PlantUMLVisitor = CodeGen.PlantUMLVisitor;
3030
const TypescriptVisitor = CodeGen.TypescriptVisitor;
3131
const XmlSchemaVisitor = CodeGen.XmlSchemaVisitor;
3232

33-
const defaultSystemContent = `namespace org.accordproject.base
34-
abstract asset Asset { }
35-
abstract participant Participant { }
36-
abstract transaction Transaction identified by transactionId {
37-
o String transactionId
38-
}
39-
abstract event Event identified by eventId {
40-
o String eventId
41-
}`;
42-
const defaultSystemName = '@org.accordproject.base';
43-
4433
/**
4534
* Utility class that implements the commands exposed by the CLI.
4635
* @class
4736
* @memberof module:concerto-cli
4837
*/
4938
class Commands {
50-
51-
/**
52-
* Add model file
53-
*
54-
* @param {object} modelFileLoader - the model loader
55-
* @param {object} modelManager - the model manager
56-
* @param {string} ctoFile - the model file
57-
* @param {boolean} system - whether this is a system model
58-
* @return {object} the model manager
59-
*/
60-
static async addModel(modelFileLoader, modelManager, ctoFile, system) {
61-
let modelFile = null;
62-
if (system && !ctoFile) {
63-
modelFile = new ModelFile(modelManager, defaultSystemContent, defaultSystemName, true);
64-
} else if(modelFileLoader.accepts(ctoFile)) {
65-
modelFile = await modelFileLoader.load(ctoFile);
66-
} else {
67-
const content = fs.readFileSync(ctoFile, 'utf8');
68-
modelFile = new ModelFile(modelManager, content, ctoFile);
69-
}
70-
71-
if (system) {
72-
modelManager.addModelFile(modelFile, modelFile.getName(), false, true);
73-
} else {
74-
modelManager.addModelFile(modelFile, modelFile.getName(), true, false);
75-
}
76-
77-
return modelManager;
78-
}
79-
80-
/**
81-
* Load system and models in a new model manager
82-
*
83-
* @param {string} ctoSystemFile - the system model file
84-
* @param {string[]} ctoFiles - the CTO files (can be local file paths or URLs)
85-
* @return {object} the model manager
86-
*/
87-
static async loadModelManager(ctoSystemFile, ctoFiles) {
88-
let modelManager = new ModelManager();
89-
const modelFileLoader = new DefaultModelFileLoader(modelManager);
90-
91-
// Load system model
92-
modelManager = await Commands.addModel(modelFileLoader,modelManager,ctoSystemFile,true);
93-
94-
// Load user models
95-
for( let ctoFile of ctoFiles ) {
96-
modelManager = await Commands.addModel(modelFileLoader,modelManager,ctoFile,false);
97-
}
98-
99-
// Validate update models
100-
await modelManager.updateExternalModels();
101-
return modelManager;
102-
}
103-
10439
/**
10540
* Validate a sample JSON against the model
10641
*
@@ -112,7 +47,7 @@ class Commands {
11247
static async validate(sample, ctoSystemFile, ctoFiles) {
11348
const json = JSON.parse(fs.readFileSync(sample, 'utf8'));
11449

115-
const modelManager = await Commands.loadModelManager(ctoSystemFile, ctoFiles);
50+
const modelManager = await ModelLoader.loadModelManager(ctoSystemFile, ctoFiles);
11651
const factory = new Factory(modelManager);
11752
const serializer = new Serializer(factory, modelManager);
11853

@@ -129,7 +64,7 @@ class Commands {
12964
* @param {string} output the output directory
13065
*/
13166
static async compile(target, ctoSystemFile, ctoFiles, output) {
132-
const modelManager = await Commands.loadModelManager(ctoSystemFile, ctoFiles);
67+
const modelManager = await ModelLoader.loadModelManager(ctoSystemFile, ctoFiles);
13368

13469
let visitor = null;
13570

@@ -174,7 +109,7 @@ class Commands {
174109
* @param {string} output the output directory
175110
*/
176111
static async get(ctoSystemFile, ctoFiles, output) {
177-
const modelManager = await Commands.loadModelManager(ctoSystemFile, ctoFiles);
112+
const modelManager = await ModelLoader.loadModelManager(ctoSystemFile, ctoFiles);
178113
mkdirp.sync(output);
179114
modelManager.writeModelsToFileSystem(output);
180115
return `Loaded external models in '${output}'.`;

packages/concerto-core/api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ class ValidatedResource extends Resource {
179179
+ void addArrayValue(string,string) throws Error
180180
+ void validate() throws Error
181181
}
182+
class ModelLoader {
183+
+ object loadModelManager(string,string[])
184+
}
182185
class ModelManager {
183186
+ void constructor()
184187
+ void validateModelFile(string,string) throws IllegalModelException

packages/concerto-core/changelog.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
# Note that the latest public API is documented using JSDocs and is available in api.txt.
2525
#
2626

27-
Version 0.82.1 {cb6c3b228035279e6997a4e3a34ef348} 2019-10-22
27+
Version 0.82.1 {dee013e99a3c2d6acc4eddfb00aad2a2} 2019-10-22
2828
- Make several constructors public
29+
- Add model loader utility class
2930

3031
Version 0.80.3 {6f5a9ab45943cb76732c14b11f47d044} 2019-08-24
3132
- Add Ergo option to serializer

packages/concerto-core/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports.Globalize = require('./lib/globalize');
3737
module.exports.Introspector = require('./lib/introspect/introspector');
3838
module.exports.Logger = require('./lib/logger');
3939
module.exports.ModelFile = require('./lib/introspect/modelfile');
40+
module.exports.ModelLoader = require('./lib/modelloader');
4041
module.exports.ModelManager = require('./lib/modelmanager');
4142
module.exports.DefaultModelFileLoader = require('./lib/introspect/loaders/defaultmodelfileloader');
4243
module.exports.ParseException = require('./lib/introspect/parseexception');
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
'use strict';
16+
17+
const fs = require('fs');
18+
19+
const DefaultModelFileLoader = require('./introspect/loaders/defaultmodelfileloader');
20+
const ModelFile = require('./introspect/modelfile');
21+
const ModelManager = require('./modelmanager');
22+
23+
const defaultSystemContent = `namespace org.accordproject.base
24+
abstract asset Asset { }
25+
abstract participant Participant { }
26+
abstract transaction Transaction identified by transactionId {
27+
o String transactionId
28+
}
29+
abstract event Event identified by eventId {
30+
o String eventId
31+
}`;
32+
const defaultSystemName = '@org.accordproject.base';
33+
34+
/**
35+
* Create a ModelManager from model files, with an optional system model.
36+
*
37+
* If a ctoFile is not provided, the Accord Project system model is used.
38+
*
39+
* @class
40+
* @memberof module:concerto-core
41+
*/
42+
class ModelLoader {
43+
/**
44+
* Add model file
45+
*
46+
* @param {object} modelFileLoader - the model loader
47+
* @param {object} modelManager - the model manager
48+
* @param {string} ctoFile - the model file
49+
* @param {boolean} system - whether this is a system model
50+
* @return {object} the model manager
51+
* @private
52+
*/
53+
static async addModel(modelFileLoader, modelManager, ctoFile, system) {
54+
let modelFile = null;
55+
if (system && !ctoFile) {
56+
modelFile = new ModelFile(modelManager, defaultSystemContent, defaultSystemName, true);
57+
} else if(modelFileLoader.accepts(ctoFile)) {
58+
modelFile = await modelFileLoader.load(ctoFile);
59+
} else {
60+
const content = fs.readFileSync(ctoFile, 'utf8');
61+
modelFile = new ModelFile(modelManager, content, ctoFile);
62+
}
63+
64+
if (system) {
65+
modelManager.addModelFile(modelFile, modelFile.getName(), false, true);
66+
} else {
67+
modelManager.addModelFile(modelFile, modelFile.getName(), true, false);
68+
}
69+
70+
return modelManager;
71+
}
72+
73+
/**
74+
* Load system and models in a new model manager
75+
*
76+
* @param {string} ctoSystemFile - the system model file
77+
* @param {string[]} ctoFiles - the CTO files (can be local file paths or URLs)
78+
* @return {object} the model manager
79+
*/
80+
static async loadModelManager(ctoSystemFile, ctoFiles) {
81+
let modelManager = new ModelManager();
82+
const modelFileLoader = new DefaultModelFileLoader(modelManager);
83+
84+
// Load system model
85+
modelManager = await ModelLoader.addModel(modelFileLoader,modelManager,ctoSystemFile,true);
86+
87+
// Load user models
88+
for( let ctoFile of ctoFiles ) {
89+
modelManager = await ModelLoader.addModel(modelFileLoader,modelManager,ctoFile,false);
90+
}
91+
92+
// Validate update models
93+
await modelManager.updateExternalModels();
94+
return modelManager;
95+
}
96+
97+
}
98+
99+
module.exports = ModelLoader;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
'use strict';
16+
17+
const Factory = require('../lib/factory');
18+
const ModelLoader = require('../lib/modelloader');
19+
const ModelManager = require('../lib/modelmanager');
20+
const TypeNotFoundException = require('../lib/typenotfoundexception');
21+
const Serializer = require('../lib/serializer');
22+
23+
const chai = require('chai');
24+
chai.use(require('chai-things'));
25+
chai.use(require('chai-as-promised'));
26+
27+
describe('ModelLoader', () => {
28+
29+
let modelBase = './test/data/model/model-base.cto';
30+
let modelUrl = 'https://models.accordproject.org/patents/patent.cto';
31+
32+
beforeEach(() => {
33+
});
34+
35+
afterEach(() => {
36+
});
37+
38+
describe('#loadModelFromFile', function() {
39+
it('should load models', async function() {
40+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
41+
(function() {
42+
modelManager.getType('String');
43+
}).should.throw(TypeNotFoundException);
44+
});
45+
46+
it('should throw an error for a namespace that does not exist', async function() {
47+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
48+
(function() {
49+
modelManager.getType('org.acme.nosuchns.SimpleAsset');
50+
}).should.throw(TypeNotFoundException, /org.acme.nosuchns/);
51+
});
52+
53+
it('should throw an error for an empty namespace', async function() {
54+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
55+
(function() {
56+
modelManager.getType('NoSuchAsset');
57+
}).should.throw(TypeNotFoundException, /NoSuchAsset/);
58+
});
59+
60+
it('should throw an error for a type that does not exist', async function() {
61+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
62+
(function() {
63+
modelManager.getType('org.acme.base.NoSuchAsset');
64+
}).should.throw(TypeNotFoundException, /NoSuchAsset/);
65+
});
66+
67+
it('should return the class declaration for a valid type', async function() {
68+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
69+
const declaration = modelManager.getType('org.acme.base.AbstractAsset');
70+
declaration.getFullyQualifiedName().should.equal('org.acme.base.AbstractAsset');
71+
});
72+
});
73+
74+
describe('#loadModelFromUrl', function() {
75+
it('should load models', async function() {
76+
const modelManager = await ModelLoader.loadModelManager(null, [modelUrl]);
77+
(modelManager instanceof ModelManager).should.be.true;
78+
});
79+
});
80+
81+
describe('#getFactory', () => {
82+
83+
it('should return a factory', async () => {
84+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
85+
modelManager.getFactory().should.be.an.instanceOf(Factory);
86+
});
87+
88+
});
89+
90+
describe('#getSerializer', () => {
91+
92+
it('should return a serializer', async () => {
93+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
94+
modelManager.getSerializer().should.be.an.instanceOf(Serializer);
95+
});
96+
97+
});
98+
99+
describe('#hasInstance', () => {
100+
it('should return true for a valid ModelManager', async () => {
101+
const modelManager = await ModelLoader.loadModelManager(null, [modelBase]);
102+
(modelManager instanceof ModelManager).should.be.true;
103+
});
104+
});
105+
106+
});

packages/concerto-core/test/modelmanager.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414

1515
'use strict';
1616

17+
const fs = require('fs');
18+
1719
const AssetDeclaration = require('../lib/introspect/assetdeclaration');
1820
const ConceptDeclaration = require('../lib/introspect/conceptdeclaration');
1921
const DecoratorFactory = require('../lib/introspect/decoratorfactory');
2022
const EnumDeclaration = require('../lib/introspect/enumdeclaration');
2123
const EventDeclaration = require('../lib/introspect/eventdeclaration');
2224
const Factory = require('../lib/factory');
23-
const fs = require('fs');
2425
const ModelFile = require('../lib/introspect/modelfile');
2526
const ModelFileDownloader = require('../lib/introspect/loaders/modelfiledownloader');
2627
const ModelManager = require('../lib/modelmanager');

0 commit comments

Comments
 (0)