Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit 5c71092

Browse files
authored
Enable namespace changes to model files via composer (#1045)
* add new namespace delete function and tests * extend test coverage
1 parent 71d773d commit 5c71092

File tree

11 files changed

+574
-99
lines changed

11 files changed

+574
-99
lines changed

packages/composer-common/lib/modelmanager.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,26 @@ class ModelManager {
6161
return visitor.visit(this, parameters);
6262
}
6363

64+
/**
65+
* Validates a Composer file (as a string) to the ModelManager.
66+
* Composer files have a single namespace.
67+
*
68+
* Note that if there are dependencies between multiple files the files
69+
* must be added in dependency order, or the addModelFiles method can be
70+
* used to add a set of files irrespective of dependencies.
71+
* @param {string} modelFile - The Composer file as a string
72+
* @param {string} fileName - an optional file name to associate with the model file
73+
* @throws {IllegalModelException}
74+
*/
75+
validateModelFile(modelFile, fileName) {
76+
if (typeof modelFile === 'string') {
77+
let m = new ModelFile(this, modelFile, fileName);
78+
m.validate();
79+
} else {
80+
modelFile.validate();
81+
}
82+
}
83+
6484
/**
6585
* Adds a Composer file (as a string) to the ModelManager.
6686
* Composer files have a single namespace. If a Composer file with the

packages/composer-common/test/modelmanager.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ describe('ModelManager', () => {
3232
let modelBase = fs.readFileSync('./test/data/model/model-base.cto', 'utf8');
3333
let farm2fork = fs.readFileSync('./test/data/model/farm2fork.cto', 'utf8');
3434
let concertoModel = fs.readFileSync('./test/data/model/concerto.cto', 'utf8');
35+
let invalidModel = fs.readFileSync('./test/data/model/invalid.cto', 'utf8');
3536
let modelManager;
3637

3738
beforeEach(() => {
@@ -51,6 +52,26 @@ describe('ModelManager', () => {
5152

5253
});
5354

55+
describe('#validateModelFile', () => {
56+
57+
it('should validate model files from strings', () => {
58+
modelBase.should.not.be.null;
59+
modelManager.validateModelFile(modelBase);
60+
});
61+
62+
it('should validate model files from objects', () => {
63+
modelBase.should.not.be.null;
64+
modelManager.validateModelFile(modelBase, 'model-base.cto');
65+
});
66+
67+
it('should fail validation of invalid model files from objects', () => {
68+
invalidModel.should.not.be.null;
69+
(() => {
70+
modelManager.validateModelFile(invalidModel);
71+
}).should.throw();
72+
});
73+
});
74+
5475
describe('#addModelFile', () => {
5576

5677
it('should add a model file from a string', () => {

packages/composer-playground/src/app/add-file/add-file.component.spec.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ describe('AddFileComponent', () => {
295295
namespace org.acme.model`],
296296
{type: 'text/plain'}
297297
);
298-
let file = new File([b], 'lib/org.acme.model.cto');
298+
let file = new File([b], 'org.acme.model.cto');
299299
let dataBuffer = new Buffer(`/**
300300
* New model file
301301
*/
@@ -307,7 +307,7 @@ namespace org.acme.model`);
307307
component.businessNetwork = mockBusinessNetwork;
308308

309309
component.changeCurrentFileType();
310-
component.currentFileName.should.equal('lib/org.acme.model.cto');
310+
component.currentFileName.should.equal('org.acme.model.cto');
311311
component.currentFile.should.deep.equal(mockModel);
312312

313313
}));
@@ -322,7 +322,7 @@ namespace org.acme.model`);
322322
namespace org.acme.model`],
323323
{type: 'text/plain'}
324324
);
325-
let file = new File([b], 'lib/org.acme.model.cto');
325+
let file = new File([b], 'org.acme.model.cto');
326326
let dataBuffer = new Buffer(`/**
327327
* New model file
328328
*/
@@ -337,8 +337,45 @@ namespace org.acme.model`);
337337
component.businessNetwork = mockBusinessNetwork;
338338

339339
component.changeCurrentFileType();
340-
component.currentFileName.should.equal('lib/org.acme.model1.cto');
340+
component.currentFileName.should.equal('org.acme.model0.cto');
341341
});
342+
343+
it('should fill in template model name indices for a cto file name', async(() => {
344+
let mockFile = sinon.createStubInstance(ModelFile);
345+
mockFile.getNamespace.returns('org.acme.model');
346+
let mockFile0 = sinon.createStubInstance(ModelFile);
347+
mockFile0.getNamespace.returns('org.acme.model0');
348+
let mockFile1 = sinon.createStubInstance(ModelFile);
349+
mockFile1.getNamespace.returns('org.acme.model1');
350+
let mockFile3 = sinon.createStubInstance(ModelFile);
351+
mockFile3.getNamespace.returns('org.acme.model3');
352+
let mockFile4 = sinon.createStubInstance(ModelFile);
353+
mockFile4.getNamespace.returns('org.acme.model4');
354+
mockModelManager.getModelFiles.returns([mockFile, mockFile0, mockFile1, mockFile3, mockFile4]);
355+
356+
let b = new Blob(
357+
[`/**
358+
* New model file
359+
*/
360+
361+
namespace org.acme.model`],
362+
{type: 'text/plain'}
363+
);
364+
let file = new File([b], 'org.acme.model.cto');
365+
let dataBuffer = new Buffer(`/**
366+
* New model file
367+
*/
368+
369+
namespace org.acme.model`);
370+
371+
let mockModel = new ModelFile(mockModelManager, dataBuffer.toString(), file.name);
372+
373+
component.fileType = 'cto';
374+
component.businessNetwork = mockBusinessNetwork;
375+
376+
component.changeCurrentFileType();
377+
component.currentFileName.should.equal('org.acme.model2.cto');
378+
}));
342379
});
343380

344381
describe('#removeFile', () => {

packages/composer-playground/src/app/add-file/add-file.component.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,33 +115,36 @@ export class AddFileComponent {
115115
*/`;
116116
let scriptManager = this.businessNetwork.getScriptManager();
117117
let existingScripts = scriptManager.getScripts();
118-
let filteredScripts = existingScripts.filter((script) => {
119-
let pattern = new RegExp(this.addScriptFileName + '\\d*' + this.addScriptFileExtension);
120-
return pattern.test(script.getIdentifier());
121-
});
122-
123-
let numScripts;
124-
numScripts = filteredScripts.length === 0 ? '' : filteredScripts.length;
125-
this.currentFile = scriptManager.createScript(this.addScriptFileName + numScripts + this.addScriptFileExtension, 'JS', code);
118+
let increment = 0;
119+
120+
let scriptName = this.addScriptFileName;
121+
122+
while ( existingScripts.findIndex((file) => file.getIdentifier() === scriptName) !== -1 ) {
123+
scriptName = this.addScriptFileName + increment;
124+
increment++;
125+
}
126+
127+
this.currentFile = scriptManager.createScript(scriptName, 'JS', code);
126128
this.currentFileName = this.currentFile.getIdentifier();
127129
} else {
128130
let modelManager = this.businessNetwork.getModelManager();
129131
let existingModels = modelManager.getModelFiles();
130-
let filteredModels = existingModels.filter((model) => {
131-
let pattern = new RegExp(this.addModelFileName + '\\d*' + this.addModelFileExtension);
132-
return pattern.test(model.getName());
133-
});
132+
let increment = 0;
134133

135-
let numModels = filteredModels.length === 0 ? '' : filteredModels.length;
134+
let newModelNamespace = this.addModelNamespace;
135+
while ( existingModels.findIndex((file) => file.getNamespace() === newModelNamespace) !== -1 ) {
136+
newModelNamespace = this.addModelNamespace + increment;
137+
increment++;
138+
}
136139

137140
let code =
138141
`/**
139142
* New model file
140143
*/
141144
142-
namespace ${this.addModelNamespace + numModels}`;
145+
namespace ${newModelNamespace}`;
143146

144-
this.currentFile = new ModelFile(modelManager, code, this.addModelFileName + numModels + this.addModelFileExtension);
147+
this.currentFile = new ModelFile(modelManager, code, newModelNamespace + this.addModelFileExtension);
145148
this.currentFileName = this.currentFile.getFileName();
146149
}
147150
}

packages/composer-playground/src/app/editor/editor.component.html

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@
44
<div class="side-bar-nav">
55
<ul>
66
<li *ngFor="let file of files" [class.active]="file.id === currentFile.id" (click)="setCurrentFile(file)">
7-
<h3 *ngIf="file.package">Package Details</h3>
8-
<h3 *ngIf="file.model">Model File</h3>
9-
<h3 *ngIf="file.script">Script File</h3>
10-
<h3 *ngIf="file.acl">Access Control</h3>
11-
<h3 *ngIf="file.readme">About</h3>
12-
<div title="{{file.displayID}}">{{file.displayID}}</div>
7+
<div class="flex-container">
8+
<div class="flex">
9+
<h3 [class.error]="file.invalid" *ngIf="file.package">Package Details</h3>
10+
<h3 [class.error]="file.invalid" *ngIf="file.model">Model File</h3>
11+
<h3 [class.error]="file.invalid" *ngIf="file.script">Script File</h3>
12+
<h3 [class.error]="file.invalid" *ngIf="file.acl">Access Control</h3>
13+
<h3 [class.error]="file.invalid" *ngIf="file.readme">About</h3>
14+
<div [class.error]="file.invalid" title="{{file.displayID}}">{{file.displayID}}</div>
15+
</div>
16+
<div *ngIf="file.invalid" class="error_dot">
17+
<svg class="ibm-icon" aria-hidden="true">
18+
<use xlink:href="#icon-error_dot"></use>
19+
</svg>
20+
</div>
21+
</div>
1322
</li>
1423
</ul>
1524
</div>

packages/composer-playground/src/app/editor/editor.component.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ app-editor {
66
width: 100%;
77

88
.side-bar {
9+
display: flex;
10+
11+
.error_dot {
12+
align-self: center;
13+
}
14+
915
.files {
1016
& > * {
1117
padding: $space-medium $space-large;
@@ -18,6 +24,10 @@ app-editor {
1824

1925
.side-bar-nav {
2026
padding: 0;
27+
28+
.error {
29+
color: $first-warning;
30+
}
2131
}
2232
}
2333

0 commit comments

Comments
 (0)