Skip to content

Commit 8f05541

Browse files
authored
feat(analysis) scalar change analysis (#704)
* feat(analysis) scalar change analysis Signed-off-by: Dan Selman <[email protected]>
1 parent d2e7c1a commit 8f05541

23 files changed

+350
-8
lines changed

packages/concerto-analysis/src/compare-config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ export const defaultCompareConfig: CompareConfig = {
6161
'property-validator-removed': CompareResult.PATCH,
6262
'property-validator-changed': CompareResult.MAJOR,
6363
'map-key-type-changed': CompareResult.MAJOR,
64-
'map-value-type-changed': CompareResult.MAJOR
64+
'map-value-type-changed': CompareResult.MAJOR,
65+
'scalar-extends-changed': CompareResult.MAJOR,
66+
'scalar-validator-added' : CompareResult.MAJOR,
67+
'scalar-validator-removed' : CompareResult.PATCH,
68+
'scalar-validator-changed' : CompareResult.MAJOR,
6569
}
6670
};

packages/concerto-analysis/src/compare-utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* limitations under the License.
1313
*/
1414

15-
import { ClassDeclaration, EnumValueDeclaration, Field, MapDeclaration, NumberValidator, Property, RelationshipDeclaration, StringValidator, Validator } from '@accordproject/concerto-core';
15+
import { ClassDeclaration, EnumValueDeclaration, Field, MapDeclaration, ScalarDeclaration, NumberValidator, Property, RelationshipDeclaration, StringValidator, Validator } from '@accordproject/concerto-core';
1616
import Declaration from '@accordproject/concerto-core/types/lib/introspect/declaration';
1717

1818
export function getDeclarationType(declaration: Declaration) {
@@ -34,6 +34,11 @@ export function getDeclarationType(declaration: Declaration) {
3434
}
3535
} else if (declaration instanceof MapDeclaration) {
3636
return 'map';
37+
} else if (declaration instanceof ScalarDeclaration) {
38+
return 'scalar';
39+
}
40+
else {
41+
throw new Error(`unknown declaration type "${declaration}"`);
3742
}
3843
}
3944

packages/concerto-analysis/src/compare.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* limitations under the License.
1313
*/
1414

15-
import { ClassDeclaration, MapDeclaration, ModelFile, Property } from '@accordproject/concerto-core';
15+
import { ClassDeclaration, MapDeclaration, ModelFile, Property, ScalarDeclaration } from '@accordproject/concerto-core';
1616
import { CompareConfig, CompareResult, defaultCompareConfig } from './compare-config';
1717
import { CompareFinding } from './compare-message';
1818
import { CompareResults } from './compare-results';
@@ -69,14 +69,26 @@ export class Compare {
6969
return b.filter(bItem => !a.some(aItem => bItem.getName() === aItem.getName()));
7070
}
7171

72+
private getAddedScalarDeclarations(a: ScalarDeclaration[], b: ScalarDeclaration[]): ScalarDeclaration[] {
73+
return b.filter(bItem => !a.some(aItem => bItem.getName() === aItem.getName()));
74+
}
75+
7276
private getMatchingMapDeclarations(a: MapDeclaration[], b: MapDeclaration[]): [a: MapDeclaration, b: MapDeclaration][] {
7377
return a.map(aItem => [aItem, b.find(bItem => aItem.getName() === bItem.getName())]).filter(([, b]) => !!b) as [MapDeclaration, MapDeclaration][];
7478
}
7579

80+
private getMatchingScalarDeclarations(a: ScalarDeclaration[], b: ScalarDeclaration[]): [a: ScalarDeclaration, b: ScalarDeclaration][] {
81+
return a.map(aItem => [aItem, b.find(bItem => aItem.getName() === bItem.getName())]).filter(([, b]) => !!b) as [ScalarDeclaration, ScalarDeclaration][];
82+
}
83+
7684
private getRemovedMapDeclarations(a: MapDeclaration[], b: MapDeclaration[]): MapDeclaration[] {
7785
return a.filter(aItem => !b.some(bItem => aItem.getName() === bItem.getName()));
7886
}
7987

88+
private getRemovedScalarDeclarations(a: ScalarDeclaration[], b: ScalarDeclaration[]): ScalarDeclaration[] {
89+
return a.filter(aItem => !b.some(bItem => aItem.getName() === bItem.getName()));
90+
}
91+
8092
private getAddedClassDeclarations(a: ClassDeclaration[], b: ClassDeclaration[]): ClassDeclaration[] {
8193
return b.filter(bItem => !a.some(aItem => bItem.getName() === aItem.getName()));
8294
}
@@ -98,12 +110,26 @@ export class Compare {
98110
removed.forEach(a => comparers.forEach(comparer => comparer.compareMapDeclaration?.(a, undefined)));
99111
}
100112

113+
private compareScalarDeclarations(comparers: Comparer[], a: ScalarDeclaration[], b: ScalarDeclaration[]) {
114+
const added = this.getAddedScalarDeclarations(a, b);
115+
const matching = this.getMatchingScalarDeclarations(a, b);
116+
const removed = this.getRemovedScalarDeclarations(a, b);
117+
added.forEach(b => comparers.forEach(comparer => comparer.compareScalarDeclaration?.(undefined, b)));
118+
matching.forEach(([a, b]) => comparers.forEach(comparer => comparer.compareScalarDeclaration?.(a, b)));
119+
removed.forEach(a => comparers.forEach(comparer => comparer.compareScalarDeclaration?.(a, undefined)));
120+
}
121+
122+
101123
private compareClassDeclaration(comparers: Comparer[], a: ClassDeclaration, b: ClassDeclaration) {
102124
comparers.forEach(comparer => comparer.compareClassDeclaration?.(a, b));
103125
// MapDeclarations do not contain properties, nothing to compare.
104126
if(a instanceof MapDeclaration || b instanceof MapDeclaration) {
105127
return;
106128
}
129+
// ScalarDeclarations do not contain properties, nothing to compare.
130+
if(a instanceof ScalarDeclaration || b instanceof ScalarDeclaration) {
131+
return;
132+
}
107133
this.compareProperties(comparers, a.getOwnProperties(), b.getOwnProperties());
108134
}
109135

@@ -120,6 +146,7 @@ export class Compare {
120146
comparers.forEach(comparer => comparer.compareModelFiles?.(a, b));
121147
this.compareClassDeclarations(comparers, a.getAllDeclarations(), b.getAllDeclarations());
122148
this.compareMapDeclarations(comparers, a.getMapDeclarations(), b.getMapDeclarations());
149+
this.compareScalarDeclarations(comparers, a.getScalarDeclarations(), b.getScalarDeclarations());
123150
}
124151

125152
private buildResults(findings: CompareFinding[]) {

packages/concerto-analysis/src/comparer.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* limitations under the License.
1313
*/
1414

15-
import { ClassDeclaration, MapDeclaration, ModelFile, Property } from '@accordproject/concerto-core';
15+
import { ClassDeclaration, MapDeclaration, ModelFile, Property, ScalarDeclaration } from '@accordproject/concerto-core';
1616
import { CompareContext } from './compare-context';
1717

1818
/**
@@ -42,6 +42,15 @@ export type Comparer = {
4242
*/
4343
compareMapDeclaration?: (a: MapDeclaration | undefined, b: MapDeclaration | undefined) => void;
4444

45+
/**
46+
* Called to compare two scalar declarations. If a is undefined, but b is defined, then this scalar declaration was
47+
* created in the second model. If a is defined, but b is undefined, then this map declaration was removed in the
48+
* second model.
49+
* @param a The first scalar declaration for comparision, or undefined if it is undefined in the first model.
50+
* @param b The second scalar declaration for comparision, or undefined if it is undefined in the second model.
51+
*/
52+
compareScalarDeclaration?: (a: ScalarDeclaration | undefined, b: ScalarDeclaration | undefined) => void;
53+
4554
/**
4655
* Called to compare two properties. If a is undefined, but b is definecd, then this property was
4756
* created in the second model. If a is defined, but b is undefined, then this property was removed in the

packages/concerto-analysis/src/comparers/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import { classDeclarationComparerFactories } from './class-declarations';
1616
import { modelFileComparerFactories } from './model-files';
1717
import { propertyComparerFactories } from './properties';
1818
import { mapDeclarationComparerFactories } from './map-declarations';
19+
import { scalarDeclarationComparerFactories } from './scalar-declarations';
1920

2021
export const comparerFactories = [
2122
...classDeclarationComparerFactories,
2223
...propertyComparerFactories,
2324
...modelFileComparerFactories,
24-
...mapDeclarationComparerFactories
25+
...mapDeclarationComparerFactories,
26+
...scalarDeclarationComparerFactories
2527
];
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
import { ComparerFactory } from '../comparer';
16+
import { getValidatorType } from '../compare-utils';
17+
18+
const scalarDeclarationExtendsChanged: ComparerFactory = (context) => ({
19+
compareScalarDeclaration: (a, b) => {
20+
21+
if (!a || !b) {
22+
return;
23+
}
24+
25+
if(a.getType() !== b.getType()) {
26+
context.report({
27+
key: 'scalar-extends-changed',
28+
message: `The scalar extends was changed from "${a.getType()}" to "${b.getType()}"`,
29+
element: b.getName()
30+
});
31+
}
32+
},
33+
});
34+
35+
const scalarValidatorChanged: ComparerFactory = (context) => ({
36+
compareScalarDeclaration: (a, b) => {
37+
if (!a || !b) {
38+
return;
39+
}
40+
const aValidator = a.getValidator();
41+
const bValidator = b.getValidator();
42+
if (!aValidator && !bValidator) {
43+
return;
44+
} else if (!aValidator && bValidator) {
45+
const bValidatorType = getValidatorType(bValidator);
46+
context.report({
47+
key: 'scalar-validator-added',
48+
message: `A ${bValidatorType} validator was added to the scalar "${a.getName()}"`,
49+
element: a.getName()
50+
});
51+
return;
52+
} else if (aValidator && !bValidator) {
53+
const aValidatorType = getValidatorType(aValidator);
54+
context.report({
55+
key: 'scalar-validator-removed',
56+
message: `A ${aValidatorType} validator was removed from the scalar "${a.getName()}"`,
57+
element: a.getName()
58+
});
59+
return;
60+
} else if (!aValidator.compatibleWith(bValidator)) {
61+
const aValidatorType = getValidatorType(aValidator);
62+
context.report({
63+
key: 'scalar-validator-changed',
64+
message: `A ${aValidatorType} validator for the scalar "${a.getName()}" was changed and is no longer compatible`,
65+
element: a.getName()
66+
});
67+
return;
68+
}
69+
}
70+
});
71+
72+
export const scalarDeclarationComparerFactories = [scalarDeclarationExtendsChanged, scalarValidatorChanged];
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+
scalar Thing extends String
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+
scalar Thing extends Integer
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+
scalar Thing extends Integer range=[-1,1]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+
scalar Thing extends Integer range=[-2,1]

0 commit comments

Comments
 (0)