From d950102d5c6a2560ae469ecbaee8a72292408bb0 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Mon, 21 Aug 2023 22:01:59 +0100 Subject: [PATCH 01/15] feat(maps): export MapDeclaration type definitions Signed-off-by: jonathan.casey --- packages/concerto-core/index.js | 8 ++++++++ packages/concerto-core/types/index.d.ts | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/concerto-core/index.js b/packages/concerto-core/index.js index 0f4f8b5fa..b8387461e 100644 --- a/packages/concerto-core/index.js +++ b/packages/concerto-core/index.js @@ -42,6 +42,11 @@ const ParticipantDeclaration = require('./lib/introspect/participantdeclaration' const TransactionDeclaration = require('./lib/introspect/transactiondeclaration'); const ScalarDeclaration = require('./lib/introspect/scalardeclaration'); +// MapDeclaration +const MapDeclaration = require('./lib/introspect/mapdeclaration'); +const MapKeyType = require('./lib/introspect/mapkeytype'); +const MapValueType = require('./lib/introspect/mapvaluetype'); + // Properties const Property = require('./lib/introspect/property'); const Field = require('./lib/introspect/field'); @@ -118,6 +123,9 @@ module.exports = { ParticipantDeclaration, TransactionDeclaration, ScalarDeclaration, + MapDeclaration, + MapKeyType, + MapValueType, Property, Field, EnumDeclaration, diff --git a/packages/concerto-core/types/index.d.ts b/packages/concerto-core/types/index.d.ts index 4396d18cf..d35663394 100644 --- a/packages/concerto-core/types/index.d.ts +++ b/packages/concerto-core/types/index.d.ts @@ -14,6 +14,9 @@ import EventDeclaration = require("./lib/introspect/eventdeclaration"); import ParticipantDeclaration = require("./lib/introspect/participantdeclaration"); import TransactionDeclaration = require("./lib/introspect/transactiondeclaration"); import ScalarDeclaration = require("./lib/introspect/scalardeclaration"); +import MapDeclaration = require("./lib/introspect/mapdeclaration"); +import MapKeyType = require("./lib/introspect/mapkeytype"); +import MapValueType = require("./lib/introspect/mapvaluetype"); import Property = require("./lib/introspect/property"); import Field = require("./lib/introspect/field"); import EnumDeclaration = require("./lib/introspect/enumdeclaration"); @@ -41,4 +44,4 @@ export const version: { name: string; version: string; }; -export { SecurityException, IllegalModelException, TypeNotFoundException, MetamodelException, Decorator, DecoratorFactory, DecoratorManager, ClassDeclaration, IdentifiedDeclaration, AssetDeclaration, ConceptDeclaration, EnumValueDeclaration, EventDeclaration, ParticipantDeclaration, TransactionDeclaration, ScalarDeclaration, Property, Field, EnumDeclaration, RelationshipDeclaration, Validator, NumberValidator, StringValidator, Typed, Identifiable, Relationship, Resource, Factory, Globalize, Introspector, ModelFile, ModelManager, Serializer, ModelUtil, ModelLoader, DateTimeUtil, Concerto, MetaModel }; +export { SecurityException, IllegalModelException, TypeNotFoundException, MetamodelException, Decorator, DecoratorFactory, DecoratorManager, ClassDeclaration, IdentifiedDeclaration, AssetDeclaration, ConceptDeclaration, EnumValueDeclaration, EventDeclaration, ParticipantDeclaration, TransactionDeclaration, ScalarDeclaration, MapDeclaration, MapKeyType, MapValueType, Property, Field, EnumDeclaration, RelationshipDeclaration, Validator, NumberValidator, StringValidator, Typed, Identifiable, Relationship, Resource, Factory, Globalize, Introspector, ModelFile, ModelManager, Serializer, ModelUtil, ModelLoader, DateTimeUtil, Concerto, MetaModel }; From 0277311f92e8fe14aa184d9d4a5d3d5ec57117dd Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 20:07:14 +0100 Subject: [PATCH 02/15] feat(maps): update JSDoc and type definition Signed-off-by: jonathan.casey --- packages/concerto-core/lib/introspect/mapdeclaration.js | 4 ++-- .../types/lib/introspect/mapdeclaration.d.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/concerto-core/lib/introspect/mapdeclaration.js b/packages/concerto-core/lib/introspect/mapdeclaration.js index a83e735aa..2ddc54373 100644 --- a/packages/concerto-core/lib/introspect/mapdeclaration.js +++ b/packages/concerto-core/lib/introspect/mapdeclaration.js @@ -126,7 +126,7 @@ class MapDeclaration extends Declaration { /** * Returns the type of the Map key property. * - * @return {string} the Map key property + * @return {MapKeyType} the Map key property */ getKey() { return this.key; @@ -135,7 +135,7 @@ class MapDeclaration extends Declaration { /** * Returns the type of the Map Value property. * - * @return {string} the Map Value property + * @return {MapValueType} the Map Value property */ getValue() { return this.value; diff --git a/packages/concerto-core/types/lib/introspect/mapdeclaration.d.ts b/packages/concerto-core/types/lib/introspect/mapdeclaration.d.ts index 6932af382..b2d219618 100644 --- a/packages/concerto-core/types/lib/introspect/mapdeclaration.d.ts +++ b/packages/concerto-core/types/lib/introspect/mapdeclaration.d.ts @@ -59,15 +59,15 @@ declare class MapDeclaration { /** * Returns the type of the Map key property. * - * @return {string} the Map key property + * @return {MapKeyType} the Map key property */ - getKey(): string; + getKey(): MapKeyType; /** * Returns the type of the Map Value property. * - * @return {string} the Map Value property + * @return {MapValueType} the Map Value property */ - getValue(): string; + getValue(): MapValueType; /** * Returns the string representation of this class * @return {String} the string representation of the class From 45470aee446597d3a97ba9db75869a69ce0d1679 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 20:20:04 +0100 Subject: [PATCH 03/15] feat(maps): adds map-declaration comparer Signed-off-by: jonathan.casey --- .../concerto-analysis/src/compare-config.ts | 2 + .../concerto-analysis/src/comparers/index.ts | 4 +- .../src/comparers/map-declarations.ts | 42 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 packages/concerto-analysis/src/comparers/map-declarations.ts diff --git a/packages/concerto-analysis/src/compare-config.ts b/packages/concerto-analysis/src/compare-config.ts index c551e32cb..4ee56dbb8 100644 --- a/packages/concerto-analysis/src/compare-config.ts +++ b/packages/concerto-analysis/src/compare-config.ts @@ -60,5 +60,7 @@ export const defaultCompareConfig: CompareConfig = { 'property-validator-added': CompareResult.MAJOR, 'property-validator-removed': CompareResult.PATCH, 'property-validator-changed': CompareResult.MAJOR, + 'map-key-type-changed': CompareResult.MAJOR, + 'map-value-type-changed': CompareResult.MAJOR } }; diff --git a/packages/concerto-analysis/src/comparers/index.ts b/packages/concerto-analysis/src/comparers/index.ts index 37ea72c4c..53fee5de5 100644 --- a/packages/concerto-analysis/src/comparers/index.ts +++ b/packages/concerto-analysis/src/comparers/index.ts @@ -15,9 +15,11 @@ import { classDeclarationComparerFactories } from './class-declarations'; import { modelFileComparerFactories } from './model-files'; import { propertyComparerFactories } from './properties'; +import { mapDeclarationComparerFactories } from './map-declarations'; export const comparerFactories = [ ...classDeclarationComparerFactories, ...propertyComparerFactories, - ...modelFileComparerFactories + ...modelFileComparerFactories, + ...mapDeclarationComparerFactories ]; diff --git a/packages/concerto-analysis/src/comparers/map-declarations.ts b/packages/concerto-analysis/src/comparers/map-declarations.ts new file mode 100644 index 000000000..0dc815957 --- /dev/null +++ b/packages/concerto-analysis/src/comparers/map-declarations.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ + +import { ComparerFactory } from '../comparer'; + +const mapDeclarationTypeChanged: ComparerFactory = (context) => ({ + compareMapDeclaration: (a, b) => { + + if (!a || !b) { + return; + } + + if(a.getKey().getType() !== b.getKey().getType()) { + context.report({ + key: 'map-key-type-changed', + message: `The map key type was changed to "${b?.getKey().getType()}"`, + element: b + }); + } + + if(a.getValue().getType() !== b.getValue().getType()) { + context.report({ + key: 'map-value-type-changed', + message: `The map value type was changed to "${b?.getValue().getType()}"`, + element: b + }); + } + }, +}); + +export const mapDeclarationComparerFactories = [mapDeclarationTypeChanged]; From 2d76bfd820ee10bd3d5036bd009fba3ad03367d5 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 21:14:49 +0100 Subject: [PATCH 04/15] feat(maps): extends comparer for map declaration Signed-off-by: jonathan.casey --- packages/concerto-analysis/src/compare.ts | 28 +++++++++++++++++++++- packages/concerto-analysis/src/comparer.ts | 13 ++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/concerto-analysis/src/compare.ts b/packages/concerto-analysis/src/compare.ts index c533cb0b0..9d1fe21a4 100644 --- a/packages/concerto-analysis/src/compare.ts +++ b/packages/concerto-analysis/src/compare.ts @@ -12,7 +12,7 @@ * limitations under the License. */ -import { ClassDeclaration, ModelFile, Property } from '@accordproject/concerto-core'; +import { ClassDeclaration, MapDeclaration, ModelFile, Property } from '@accordproject/concerto-core'; import { CompareConfig, CompareResult, defaultCompareConfig } from './compare-config'; import { CompareFinding } from './compare-message'; import { CompareResults } from './compare-results'; @@ -65,6 +65,18 @@ export class Compare { removed.forEach(a => comparers.forEach(comparer => comparer.compareProperty?.(a, undefined))); } + private getAddedMapDeclarations(a: MapDeclaration[], b: MapDeclaration[]): MapDeclaration[] { + return b.filter(bItem => !a.some(aItem => bItem.getName() === aItem.getName())); + } + + private getMatchingMapDeclarations(a: MapDeclaration[], b: MapDeclaration[]): [a: MapDeclaration, b: MapDeclaration][] { + return a.map(aItem => [aItem, b.find(bItem => aItem.getName() === bItem.getName())]).filter(([, b]) => !!b) as [MapDeclaration, MapDeclaration][]; + } + + private getRemovedMapDeclarations(a: MapDeclaration[], b: MapDeclaration[]): MapDeclaration[] { + return a.filter(aItem => !b.some(bItem => aItem.getName() === bItem.getName())); + } + private getAddedClassDeclarations(a: ClassDeclaration[], b: ClassDeclaration[]): ClassDeclaration[] { return b.filter(bItem => !a.some(aItem => bItem.getName() === aItem.getName())); } @@ -77,8 +89,21 @@ export class Compare { return a.filter(aItem => !b.some(bItem => aItem.getName() === bItem.getName())); } + private compareMapDeclarations(comparers: Comparer[], a: MapDeclaration[], b: MapDeclaration[]) { + const added = this.getAddedMapDeclarations(a, b); + const matching = this.getMatchingMapDeclarations(a, b); + const removed = this.getRemovedMapDeclarations(a, b); + added.forEach(b => comparers.forEach(comparer => comparer.compareMapDeclaration?.(undefined, b))); + matching.forEach(([a, b]) => comparers.forEach(comparer => comparer.compareMapDeclaration?.(a, b))); + removed.forEach(a => comparers.forEach(comparer => comparer.compareMapDeclaration?.(a, undefined))); + } + private compareClassDeclaration(comparers: Comparer[], a: ClassDeclaration, b: ClassDeclaration) { comparers.forEach(comparer => comparer.compareClassDeclaration?.(a, b)); + // MapDeclarations do not contain properties, nothing to compare. + if(a instanceof MapDeclaration || b instanceof MapDeclaration) { + return; + } this.compareProperties(comparers, a.getOwnProperties(), b.getOwnProperties()); } @@ -94,6 +119,7 @@ export class Compare { private compareModelFiles(comparers: Comparer[], a: ModelFile, b: ModelFile) { comparers.forEach(comparer => comparer.compareModelFiles?.(a, b)); this.compareClassDeclarations(comparers, a.getAllDeclarations(), b.getAllDeclarations()); + this.compareMapDeclarations(comparers, a.getMapDeclarations(), b.getMapDeclarations()); } private buildResults(findings: CompareFinding[]) { diff --git a/packages/concerto-analysis/src/comparer.ts b/packages/concerto-analysis/src/comparer.ts index 5002f7b83..10f5b326b 100644 --- a/packages/concerto-analysis/src/comparer.ts +++ b/packages/concerto-analysis/src/comparer.ts @@ -12,7 +12,7 @@ * limitations under the License. */ -import { ClassDeclaration, ModelFile, Property } from '@accordproject/concerto-core'; +import { ClassDeclaration, MapDeclaration, ModelFile, Property } from '@accordproject/concerto-core'; import { CompareContext } from './compare-context'; /** @@ -34,7 +34,16 @@ export type Comparer = { compareClassDeclaration?: (a: ClassDeclaration | undefined, b: ClassDeclaration | undefined) => void; /** - * Called to compare two properties. If a is undefined, but b is defined, then this property was + * Called to compare two map declarations. If a is undefined, but b is defined, then this map declaration was + * created in the second model. If a is defined, but b is undefined, then this class declaration was removed in the + * second model. + * @param a The first map declaration for comparision, or undefined if it is undefined in the first model. + * @param b The second map declaration for comparision, or undefined if it is undefined in the second model. + */ + compareMapDeclaration?: (a: MapDeclaration | undefined, b: MapDeclaration | undefined) => void; + + /** + * Called to compare two properties. If a is undefined, but b is definecd, then this property was * created in the second model. If a is defined, but b is undefined, then this property was removed in the * second model. * @param a The first property for comparision, or undefined if it is undefined in the first model. From 48625d26ec9ae3df93d30adc0dfabfcb77bab20b Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 21:15:33 +0100 Subject: [PATCH 05/15] feat(maps): refactor util function Signed-off-by: jonathan.casey --- .../concerto-analysis/src/compare-utils.ts | 37 +++++++++++-------- .../src/comparers/class-declarations.ts | 11 +++--- .../src/comparers/properties.ts | 10 ++--- .../test/unit/compare-utils.test.ts | 4 +- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/concerto-analysis/src/compare-utils.ts b/packages/concerto-analysis/src/compare-utils.ts index 636877cd8..f8fe0ad36 100644 --- a/packages/concerto-analysis/src/compare-utils.ts +++ b/packages/concerto-analysis/src/compare-utils.ts @@ -12,23 +12,30 @@ * limitations under the License. */ -import { ClassDeclaration, EnumValueDeclaration, Field, NumberValidator, Property, RelationshipDeclaration, StringValidator, Validator } from '@accordproject/concerto-core'; +import { ClassDeclaration, EnumValueDeclaration, Field, MapDeclaration, NumberValidator, Property, RelationshipDeclaration, StringValidator, Validator } from '@accordproject/concerto-core'; +import Declaration from '@accordproject/concerto-core/types/lib/introspect/declaration'; -export function getClassDeclarationType(classDeclaration: ClassDeclaration) { - if (classDeclaration.isAsset()) { - return 'asset'; - } else if (classDeclaration.isConcept()) { - return 'concept'; - } else if (classDeclaration.isEnum()) { - return 'enum'; - } else if (classDeclaration.isEvent()) { - return 'event'; - } else if (classDeclaration.isParticipant()) { - return 'participant'; - } else if (classDeclaration.isTransaction()) { - return 'transaction'; +export function getDeclarationType(declaration: Declaration) { + if (declaration instanceof ClassDeclaration) { + if (declaration?.isAsset?.()) { + return 'asset'; + } else if (declaration.isConcept()) { + return 'concept'; + } else if (declaration.isEnum()) { + return 'enum'; + } else if (declaration.isEvent()) { + return 'event'; + } else if (declaration.isParticipant()) { + return 'participant'; + } else if (declaration.isTransaction()) { + return 'transaction'; + } else { + throw new Error(`unknown class declaration type "${declaration}"`); + } + } else if (declaration instanceof MapDeclaration) { + return 'map'; } else { - throw new Error(`unknown class declaration type "${classDeclaration}"`); + throw new Error(`unknown declaration type "${declaration}"`); } } diff --git a/packages/concerto-analysis/src/comparers/class-declarations.ts b/packages/concerto-analysis/src/comparers/class-declarations.ts index 24a214d98..9da52b59b 100644 --- a/packages/concerto-analysis/src/comparers/class-declarations.ts +++ b/packages/concerto-analysis/src/comparers/class-declarations.ts @@ -12,15 +12,16 @@ * limitations under the License. */ -import { getClassDeclarationType } from '../compare-utils'; +import { getDeclarationType } from '../compare-utils'; import { ComparerFactory } from '../comparer'; +// todo rename to declaration.ts , rename all classDeclaration -> declaration const classDeclarationAdded: ComparerFactory = (context) => ({ compareClassDeclaration: (a, b) => { if (a || !b) { return; } - const type = getClassDeclarationType(b); + const type = getDeclarationType(b); context.report({ key: 'class-declaration-added', message: `The ${type} "${b.getName()}" was added`, @@ -34,7 +35,7 @@ const classDeclarationRemoved: ComparerFactory = (context) => ({ if (!a || b) { return; } - const type = getClassDeclarationType(a); + const type = getDeclarationType(a); context.report({ key: 'class-declaration-removed', message: `The ${type} "${a.getName()}" was removed`, @@ -48,8 +49,8 @@ const classDeclarationTypeChanged: ComparerFactory = (context) => ({ if (!a || !b) { return; } - const aType = getClassDeclarationType(a); - const bType = getClassDeclarationType(b); + const aType = getDeclarationType(a); + const bType = getDeclarationType(b); if (aType === bType) { return; } diff --git a/packages/concerto-analysis/src/comparers/properties.ts b/packages/concerto-analysis/src/comparers/properties.ts index 8d468f531..a5118b0d6 100644 --- a/packages/concerto-analysis/src/comparers/properties.ts +++ b/packages/concerto-analysis/src/comparers/properties.ts @@ -14,7 +14,7 @@ import { EnumValueDeclaration, Field, ModelUtil } from '@accordproject/concerto-core'; import * as semver from 'semver'; -import { getClassDeclarationType, getPropertyType, getValidatorType } from '../compare-utils'; +import { getDeclarationType, getPropertyType, getValidatorType } from '../compare-utils'; import { ComparerFactory } from '../comparer'; const propertyAdded: ComparerFactory = (context) => ({ @@ -22,7 +22,7 @@ const propertyAdded: ComparerFactory = (context) => ({ if (a || !b) { return; } - const classDeclarationType = getClassDeclarationType(b.getParent()); + const classDeclarationType = getDeclarationType(b.getParent()); if (b instanceof EnumValueDeclaration) { context.report({ key: 'enum-value-added', @@ -58,7 +58,7 @@ const propertyRemoved: ComparerFactory = (context) => ({ if (!a || b) { return; } - const classDeclarationType = getClassDeclarationType(a.getParent()); + const classDeclarationType = getDeclarationType(a.getParent()); if (a instanceof EnumValueDeclaration) { context.report({ key: 'enum-value-removed', @@ -96,7 +96,7 @@ const propertyTypeChanged: ComparerFactory = (context) => ({ } const aType = getPropertyType(a); const bType = getPropertyType(b); - const classDeclarationType = getClassDeclarationType(a.getParent()); + const classDeclarationType = getDeclarationType(a.getParent()); if (aType !== bType) { context.report({ key: 'property-type-changed', @@ -197,7 +197,7 @@ const propertyValidatorChanged: ComparerFactory = (context) => ({ } const aValidator = a.getValidator(); const bValidator = b.getValidator(); - const classDeclarationType = getClassDeclarationType(a.getParent()); + const classDeclarationType = getDeclarationType(a.getParent()); if (!aValidator && !bValidator) { return; } else if (!aValidator && bValidator) { diff --git a/packages/concerto-analysis/test/unit/compare-utils.test.ts b/packages/concerto-analysis/test/unit/compare-utils.test.ts index 37608bccc..58dae5241 100644 --- a/packages/concerto-analysis/test/unit/compare-utils.test.ts +++ b/packages/concerto-analysis/test/unit/compare-utils.test.ts @@ -1,5 +1,5 @@ import { ClassDeclaration, ModelFile, ModelManager, Property, Validator, Field } from '@accordproject/concerto-core'; -import { getClassDeclarationType, getPropertyType, getValidatorType } from '../../src/compare-utils'; +import { getDeclarationType, getPropertyType, getValidatorType } from '../../src/compare-utils'; // This test suite should disappear once we port concerto-core to TypeScript because the error branches will be enforced by the transpiler. @@ -19,7 +19,7 @@ const field = new Field(classDeclaration, propertyAst); const validator = new Validator(field, undefined); test('should throw for unknown class declaration type', () => { - expect(() => getClassDeclarationType(classDeclaration)).toThrow('unknown class declaration type "ClassDeclaration {id=foo@1.0.0.undefined super=Concept enum=false abstract=false}"'); + expect(() => getDeclarationType(classDeclaration)).toThrow('unknown class declaration type "ClassDeclaration {id=foo@1.0.0.undefined super=Concept enum=false abstract=false}"'); }); test('should throw for unknown class property type', () => { From 44d484dd3d88667f16d4efffeebb833331304060 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 21:18:00 +0100 Subject: [PATCH 06/15] feat(maps): adds test coverage Signed-off-by: jonathan.casey --- .../test/fixtures/map-added.cto | 6 +++++ .../test/fixtures/map-changed-key.cto | 6 +++++ .../test/fixtures/map-changed-value.cto | 6 +++++ .../test/unit/compare.test.ts | 26 ++++++++++++++++++- 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 packages/concerto-analysis/test/fixtures/map-added.cto create mode 100644 packages/concerto-analysis/test/fixtures/map-changed-key.cto create mode 100644 packages/concerto-analysis/test/fixtures/map-changed-value.cto diff --git a/packages/concerto-analysis/test/fixtures/map-added.cto b/packages/concerto-analysis/test/fixtures/map-added.cto new file mode 100644 index 000000000..7c0f4270f --- /dev/null +++ b/packages/concerto-analysis/test/fixtures/map-added.cto @@ -0,0 +1,6 @@ +namespace org.accordproject.concerto.test@1.2.3 + +map Thing { + o String + o String +} diff --git a/packages/concerto-analysis/test/fixtures/map-changed-key.cto b/packages/concerto-analysis/test/fixtures/map-changed-key.cto new file mode 100644 index 000000000..9aa20c423 --- /dev/null +++ b/packages/concerto-analysis/test/fixtures/map-changed-key.cto @@ -0,0 +1,6 @@ +namespace org.accordproject.concerto.test@1.2.3 + +map Thing { + o DateTime + o String +} diff --git a/packages/concerto-analysis/test/fixtures/map-changed-value.cto b/packages/concerto-analysis/test/fixtures/map-changed-value.cto new file mode 100644 index 000000000..254a5bed3 --- /dev/null +++ b/packages/concerto-analysis/test/fixtures/map-changed-value.cto @@ -0,0 +1,6 @@ +namespace org.accordproject.concerto.test@1.2.3 + +map Thing { + o String + o DateTime +} diff --git a/packages/concerto-analysis/test/unit/compare.test.ts b/packages/concerto-analysis/test/unit/compare.test.ts index b77ced9e5..0f405580b 100644 --- a/packages/concerto-analysis/test/unit/compare.test.ts +++ b/packages/concerto-analysis/test/unit/compare.test.ts @@ -68,7 +68,7 @@ test('should detect a change of namespace', async () => { expect(results.result).toBe(CompareResult.ERROR); }); -['asset', 'concept', 'enum', 'event', 'participant', 'transaction'].forEach(type => { +['asset', 'concept', 'enum', 'event', 'participant', 'transaction', 'map'].forEach(type => { test(`should detect a ${type} being added`, async () => { const [a, b] = await getModelFiles('empty.cto', `${type}-added.cto`); const results = new Compare().compare(a, b); @@ -229,6 +229,30 @@ test('should detect an array changing to a scalar', async () => { expect(results.result).toBe(CompareResult.MAJOR); }); +test('should detect a map key type changing from x to y', async () => { + const [a, b] = await getModelFiles('map-added.cto', 'map-changed-key.cto'); + const results = new Compare().compare(a, b); + expect(results.findings).toEqual(expect.arrayContaining([ + expect.objectContaining({ + key: 'map-key-type-changed', + message: 'The map key type was changed to "DateTime"' + }) + ])); + expect(results.result).toBe(CompareResult.MAJOR); +}); + +test('should detect a map value type changing from x to y', async () => { + const [a, b] = await getModelFiles('map-added.cto', 'map-changed-value.cto'); + const results = new Compare().compare(a, b); + expect(results.findings).toEqual(expect.arrayContaining([ + expect.objectContaining({ + key: 'map-value-type-changed', + message: 'The map value type was changed to "DateTime"' + }) + ])); + expect(results.result).toBe(CompareResult.MAJOR); +}); + test('should detect a primitive typed field changing to a declaration typed field', async () => { const [a, b] = await getModelFiles('primitive-to-declaration-a.cto', 'primitive-to-declaration-b.cto'); const results = new Compare().compare(a, b); From aa71ccb254b8c48e9f20ad3d3e6fa76371cf9644 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 21:23:48 +0100 Subject: [PATCH 07/15] feat(maps): JSDoc Signed-off-by: jonathan.casey --- packages/concerto-analysis/src/comparer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-analysis/src/comparer.ts b/packages/concerto-analysis/src/comparer.ts index 10f5b326b..65c55d6e4 100644 --- a/packages/concerto-analysis/src/comparer.ts +++ b/packages/concerto-analysis/src/comparer.ts @@ -35,7 +35,7 @@ export type Comparer = { /** * Called to compare two map declarations. If a is undefined, but b is defined, then this map declaration was - * created in the second model. If a is defined, but b is undefined, then this class declaration was removed in the + * created in the second model. If a is defined, but b is undefined, then this map declaration was removed in the * second model. * @param a The first map declaration for comparision, or undefined if it is undefined in the first model. * @param b The second map declaration for comparision, or undefined if it is undefined in the second model. From 70a681c6d1f838b6c1eeffb08f2d89bf79da036e Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 21:25:43 +0100 Subject: [PATCH 08/15] feat(maps): adds changelog hash Signed-off-by: jonathan.casey --- packages/concerto-core/api.txt | 4 ++-- packages/concerto-core/changelog.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 00c4d5c61..896399f87 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -160,8 +160,8 @@ class MapDeclaration extends Declaration { + string getFullyQualifiedName() + ModelFile getModelFile() + string getName() - + string getKey() - + string getValue() + + MapKeyType getKey() + + MapValueType getValue() + String toString() + string declarationKind() + boolean isMapDeclaration() diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index 4073480e8..9783aafed 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,6 +24,9 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # +Version 3.10.1 {399b057614d1178c02b744184b09f845} 2023-08-22 +- Change return type for Mapdeclaration :: MapKeyType MapValueType + Version 3.10.0 {3bba55da8dbb522de132359415eb7eeb} 2023-08-11 - Add MetamodelException From 86ce6931f548a72efeea9c36fb9fa153b28526bd Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 22:56:38 +0100 Subject: [PATCH 09/15] feat(maps): add ENABLE_MAP_TYPE env var for test run Signed-off-by: jonathan.casey --- packages/concerto-analysis/test/unit/compare.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/concerto-analysis/test/unit/compare.test.ts b/packages/concerto-analysis/test/unit/compare.test.ts index 0f405580b..eb39b976c 100644 --- a/packages/concerto-analysis/test/unit/compare.test.ts +++ b/packages/concerto-analysis/test/unit/compare.test.ts @@ -70,6 +70,7 @@ test('should detect a change of namespace', async () => { ['asset', 'concept', 'enum', 'event', 'participant', 'transaction', 'map'].forEach(type => { test(`should detect a ${type} being added`, async () => { + process.env.ENABLE_MAP_TYPE = 'true'; // TODO Remove on release of MapType const [a, b] = await getModelFiles('empty.cto', `${type}-added.cto`); const results = new Compare().compare(a, b); expect(results.findings).toEqual(expect.arrayContaining([ @@ -230,6 +231,7 @@ test('should detect an array changing to a scalar', async () => { }); test('should detect a map key type changing from x to y', async () => { + process.env.ENABLE_MAP_TYPE = 'true'; // TODO Remove on release of MapType const [a, b] = await getModelFiles('map-added.cto', 'map-changed-key.cto'); const results = new Compare().compare(a, b); expect(results.findings).toEqual(expect.arrayContaining([ @@ -242,6 +244,7 @@ test('should detect a map key type changing from x to y', async () => { }); test('should detect a map value type changing from x to y', async () => { + process.env.ENABLE_MAP_TYPE = 'true'; // TODO Remove on release of MapType const [a, b] = await getModelFiles('map-added.cto', 'map-changed-value.cto'); const results = new Compare().compare(a, b); expect(results.findings).toEqual(expect.arrayContaining([ From 877d232b248445377fa09e031463684beb4cfe32 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 23:17:01 +0100 Subject: [PATCH 10/15] feat(maps): remove improbable branch Signed-off-by: jonathan.casey --- packages/concerto-analysis/src/compare-utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/concerto-analysis/src/compare-utils.ts b/packages/concerto-analysis/src/compare-utils.ts index f8fe0ad36..3720c71d9 100644 --- a/packages/concerto-analysis/src/compare-utils.ts +++ b/packages/concerto-analysis/src/compare-utils.ts @@ -34,8 +34,6 @@ export function getDeclarationType(declaration: Declaration) { } } else if (declaration instanceof MapDeclaration) { return 'map'; - } else { - throw new Error(`unknown declaration type "${declaration}"`); } } From 2f61895f2a93b60597bb2cfe195898db02eaf996 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Tue, 22 Aug 2023 23:29:20 +0100 Subject: [PATCH 11/15] feat(maps): tighten optional chain call Signed-off-by: jonathan.casey --- packages/concerto-analysis/src/compare-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-analysis/src/compare-utils.ts b/packages/concerto-analysis/src/compare-utils.ts index 3720c71d9..f0d8b926e 100644 --- a/packages/concerto-analysis/src/compare-utils.ts +++ b/packages/concerto-analysis/src/compare-utils.ts @@ -17,7 +17,7 @@ import Declaration from '@accordproject/concerto-core/types/lib/introspect/decla export function getDeclarationType(declaration: Declaration) { if (declaration instanceof ClassDeclaration) { - if (declaration?.isAsset?.()) { + if (declaration.isAsset()) { return 'asset'; } else if (declaration.isConcept()) { return 'concept'; From f29b040187ae4f4d63443bdfcd6b84782b686870 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Wed, 23 Aug 2023 09:02:36 +0100 Subject: [PATCH 12/15] feat(maps): adds additional assertion to satisfy coveralls Signed-off-by: jonathan.casey --- packages/concerto-analysis/test/unit/compare.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/concerto-analysis/test/unit/compare.test.ts b/packages/concerto-analysis/test/unit/compare.test.ts index eb39b976c..d53fda75e 100644 --- a/packages/concerto-analysis/test/unit/compare.test.ts +++ b/packages/concerto-analysis/test/unit/compare.test.ts @@ -240,6 +240,7 @@ test('should detect a map key type changing from x to y', async () => { message: 'The map key type was changed to "DateTime"' }) ])); + expect(results.findings[0].message).toBe('The map key type was changed to "DateTime"'); expect(results.result).toBe(CompareResult.MAJOR); }); @@ -253,6 +254,7 @@ test('should detect a map value type changing from x to y', async () => { message: 'The map value type was changed to "DateTime"' }) ])); + expect(results.findings[0].message).toBe('The map value type was changed to "DateTime"'); expect(results.result).toBe(CompareResult.MAJOR); }); From 23a8afe1b4e7db196093752271fa5ff12871a4a9 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Wed, 23 Aug 2023 09:22:20 +0100 Subject: [PATCH 13/15] feat(maps): add more cov Signed-off-by: jonathan.casey --- packages/concerto-analysis/test/unit/compare.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/concerto-analysis/test/unit/compare.test.ts b/packages/concerto-analysis/test/unit/compare.test.ts index d53fda75e..fb8a38204 100644 --- a/packages/concerto-analysis/test/unit/compare.test.ts +++ b/packages/concerto-analysis/test/unit/compare.test.ts @@ -3,6 +3,7 @@ import { Parser } from '@accordproject/concerto-cto'; import * as fs from 'fs/promises'; import * as path from 'path'; import { Compare, CompareResult, compareResultToString } from '../../src'; +import { defaultCompareConfig } from '../../src/compare-config'; async function getModelFile(modelManager: ModelManager, fileName: string) { const filePath = path.resolve(__dirname, '..', 'fixtures', fileName); @@ -479,3 +480,8 @@ test('should detect a string validator being changed on a property (incompatible ])); expect(results.result).toBe(CompareResult.MAJOR); }); + +test('should give a MAJOR CompareResult for Map Type compare config rules)', async () => { + expect(defaultCompareConfig.rules['map-key-type-changed']).toBe(CompareResult.MAJOR); + expect(defaultCompareConfig.rules['map-value-type-changed']).toBe(CompareResult.MAJOR); +}); From 7f99c5ade6acb441c84bfa4a87b749fdc62f7430 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Wed, 23 Aug 2023 10:56:19 +0100 Subject: [PATCH 14/15] feat(maps): remove optional chaining Signed-off-by: jonathan.casey --- packages/concerto-analysis/src/comparers/map-declarations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/concerto-analysis/src/comparers/map-declarations.ts b/packages/concerto-analysis/src/comparers/map-declarations.ts index 0dc815957..159edb938 100644 --- a/packages/concerto-analysis/src/comparers/map-declarations.ts +++ b/packages/concerto-analysis/src/comparers/map-declarations.ts @@ -24,7 +24,7 @@ const mapDeclarationTypeChanged: ComparerFactory = (context) => ({ if(a.getKey().getType() !== b.getKey().getType()) { context.report({ key: 'map-key-type-changed', - message: `The map key type was changed to "${b?.getKey().getType()}"`, + message: `The map key type was changed to "${b.getKey().getType()}"`, element: b }); } @@ -32,7 +32,7 @@ const mapDeclarationTypeChanged: ComparerFactory = (context) => ({ if(a.getValue().getType() !== b.getValue().getType()) { context.report({ key: 'map-value-type-changed', - message: `The map value type was changed to "${b?.getValue().getType()}"`, + message: `The map value type was changed to "${b.getValue().getType()}"`, element: b }); } From 69820759069ae877ca0ce7749c3f2fb166247ec9 Mon Sep 17 00:00:00 2001 From: "jonathan.casey" Date: Wed, 23 Aug 2023 14:35:11 +0100 Subject: [PATCH 15/15] feat(maps): correct casing niggle Signed-off-by: jonathan.casey --- packages/concerto-core/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index 9783aafed..a99aa193e 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -25,7 +25,7 @@ # Version 3.10.1 {399b057614d1178c02b744184b09f845} 2023-08-22 -- Change return type for Mapdeclaration :: MapKeyType MapValueType +- Change return type for MapDeclaration :: MapKeyType MapValueType Version 3.10.0 {3bba55da8dbb522de132359415eb7eeb} 2023-08-11 - Add MetamodelException