From 0c4bcfec92808e0f5e14db6ada089705a060e00c Mon Sep 17 00:00:00 2001 From: StefanApfel <71712332+StefanApfel-Bentley@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:35:52 +0200 Subject: [PATCH 1/3] Added validation of schema reference versions --- .../src/Differencing/SchemaConflicts.ts | 2 + .../Differencing/SchemaDifferenceValidator.ts | 22 +++++++-- .../src/test/Differencing/Conflicts.test.ts | 47 +++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/core/ecschema-editing/src/Differencing/SchemaConflicts.ts b/core/ecschema-editing/src/Differencing/SchemaConflicts.ts index 41207b90357..43e8ea69fcf 100644 --- a/core/ecschema-editing/src/Differencing/SchemaConflicts.ts +++ b/core/ecschema-editing/src/Differencing/SchemaConflicts.ts @@ -43,6 +43,7 @@ export enum ConflictCode { ConflictingItemName = "C-001", ConflictingReferenceAlias = "C-002", + ConflictingReferenceVersion = "C-003", ConflictingBaseClass = "C-100", RemovingBaseClass = "C-101", @@ -96,6 +97,7 @@ type EcClassTypes = SchemaItemType.CustomAttributeClass | SchemaItemType.Entity export type AnySchemaDifferenceConflict = SchemaDifferenceConflict | SchemaDifferenceConflict | + SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | diff --git a/core/ecschema-editing/src/Differencing/SchemaDifferenceValidator.ts b/core/ecschema-editing/src/Differencing/SchemaDifferenceValidator.ts index e5c43fcfeea..89f8221baf4 100644 --- a/core/ecschema-editing/src/Differencing/SchemaDifferenceValidator.ts +++ b/core/ecschema-editing/src/Differencing/SchemaDifferenceValidator.ts @@ -6,7 +6,7 @@ * @module Differencing */ -import { classModifierToString, ECClass, ECClassModifier, EntityClass, Enumeration, KindOfQuantity, LazyLoadedSchemaItem, Mixin, parseClassModifier, primitiveTypeToString, Property, propertyTypeToString, Schema, SchemaItem, SchemaItemKey } from "@itwin/ecschema-metadata"; +import { classModifierToString, ECClass, ECClassModifier, EntityClass, Enumeration, KindOfQuantity, LazyLoadedSchemaItem, Mixin, parseClassModifier, primitiveTypeToString, Property, propertyTypeToString, Schema, SchemaItem, SchemaItemKey, SchemaMatchType } from "@itwin/ecschema-metadata"; import { AnyClassItemDifference, AnySchemaDifference, AnySchemaItemDifference, ClassPropertyDifference, ConstantDifference, CustomAttributeClassDifference, CustomAttributeDifference, EntityClassDifference, EntityClassMixinDifference, EnumerationDifference, EnumeratorDifference, FormatDifference, InvertedUnitDifference, KindOfQuantityDifference, MixinClassDifference, PhenomenonDifference, PropertyCategoryDifference, RelationshipClassDifference, RelationshipConstraintClassDifference, RelationshipConstraintDifference, SchemaDifference, SchemaReferenceDifference, StructClassDifference, UnitDifference, UnitSystemDifference } from "./SchemaDifference"; import { AnySchemaDifferenceConflict, ConflictCode } from "./SchemaConflicts"; import { SchemaDifferenceVisitor, SchemaDifferenceWalker } from "./SchemaDifferenceVisitor"; @@ -63,16 +63,30 @@ class SchemaDifferenceValidationVisitor implements SchemaDifferenceVisitor { */ public async visitSchemaReferenceDifference(entry: SchemaReferenceDifference) { const sourceSchemaReference = await this._sourceSchema.getReference(entry.difference.name) as Schema; - const targetSchemaReference = this._targetSchema.getReferenceNameByAlias(sourceSchemaReference.alias); - if (targetSchemaReference && targetSchemaReference !== sourceSchemaReference.name) { + const targetSchemaReferenceName = this._targetSchema.getReferenceNameByAlias(sourceSchemaReference.alias); + if (targetSchemaReferenceName && targetSchemaReferenceName !== sourceSchemaReference.name) { this.addConflict({ code: ConflictCode.ConflictingReferenceAlias, difference: entry, source: entry.difference.name, - target: targetSchemaReference, + target: targetSchemaReferenceName, description: "Target schema already references a different schema with this alias.", }); } + + const sourceSchemaKey = sourceSchemaReference.schemaKey; + const targetSchemaKey = await this._targetSchema.getReference(entry.difference.name) + .then((schema) => schema?.schemaKey); + + if(entry.changeType === "modify" && targetSchemaKey && !sourceSchemaKey.matches(targetSchemaKey, SchemaMatchType.LatestWriteCompatible)) { + return this.addConflict({ + code: ConflictCode.ConflictingReferenceVersion, + difference: entry, + description: "Schema reference cannot be updated, incompatible versions", + source: sourceSchemaKey.toString(), + target: targetSchemaKey.toString(), + }); + } } /** diff --git a/core/ecschema-editing/src/test/Differencing/Conflicts.test.ts b/core/ecschema-editing/src/test/Differencing/Conflicts.test.ts index 6dbe0948446..2421323c382 100644 --- a/core/ecschema-editing/src/test/Differencing/Conflicts.test.ts +++ b/core/ecschema-editing/src/test/Differencing/Conflicts.test.ts @@ -88,6 +88,53 @@ describe("Schema Difference Conflicts", () => { return true; }); }); + + it("should find a conflict schema references aren't compartible", async () => { + const sourceContext = new SchemaContext(); + await Schema.fromJson({ + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "ReferenceA", + version: "2.0.0", + alias: "ref", + }, sourceContext); + const sourceSchema = await Schema.fromJson({ + ...schemaHeader, + references: [ + { + name: "ReferenceA", + version: "2.0.0", + }, + ], + }, sourceContext); + + const targetContext = new SchemaContext(); + await Schema.fromJson({ + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "ReferenceA", + version: "1.0.0", + alias: "ref", + }, targetContext); + + const targetSchema = await Schema.fromJson({ + ...schemaHeader, + references: [ + { + name: "ReferenceA", + version: "1.0.0", + }, + ], + }, targetContext); + + const differences = await getSchemaDifferences(targetSchema, sourceSchema); + expect(differences.conflicts).to.have.a.lengthOf(1).and.satisfies(([conflict]: AnySchemaDifferenceConflict[]) => { + expect(conflict).to.have.a.property("code", ConflictCode.ConflictingReferenceVersion); + expect(conflict).to.have.a.property("source", "ReferenceA.02.00.00"); + expect(conflict).to.have.a.property("target", "ReferenceA.01.00.00"); + expect(conflict).to.have.a.property("description", "Schema reference cannot be updated, incompatible versions"); + expect(conflict).to.have.a.nested.property("difference.schemaType", "SchemaReference"); + return true; + }); + }); }); describe("Different schema item type conflicts", () => { From 4f23f3211129f4e87c6419de7cf732c1847f6376 Mon Sep 17 00:00:00 2001 From: StefanApfel <71712332+StefanApfel-Bentley@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:36:20 +0200 Subject: [PATCH 2/3] change message --- ...-reference-version-validation_2024-10-16-09-36.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@itwin/ecschema-editing/schemaediting-reference-version-validation_2024-10-16-09-36.json diff --git a/common/changes/@itwin/ecschema-editing/schemaediting-reference-version-validation_2024-10-16-09-36.json b/common/changes/@itwin/ecschema-editing/schemaediting-reference-version-validation_2024-10-16-09-36.json new file mode 100644 index 00000000000..b0e7d3b9654 --- /dev/null +++ b/common/changes/@itwin/ecschema-editing/schemaediting-reference-version-validation_2024-10-16-09-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/ecschema-editing", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/ecschema-editing" +} \ No newline at end of file From 873871c5645d56e8af8a56039be0c6d1fb5183c0 Mon Sep 17 00:00:00 2001 From: StefanApfel <71712332+StefanApfel-Bentley@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:38:13 +0200 Subject: [PATCH 3/3] extract api --- common/api/ecschema-editing.api.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/api/ecschema-editing.api.md b/common/api/ecschema-editing.api.md index 658db4c4c2a..76283a976f3 100644 --- a/common/api/ecschema-editing.api.md +++ b/common/api/ecschema-editing.api.md @@ -92,7 +92,7 @@ export type AnyIdentifier = ISchemaIdentifier | ISchemaItemIdentifier | IClassId export type AnySchemaDifference = SchemaDifference | SchemaReferenceDifference | AnySchemaItemDifference | AnySchemaItemPathDifference | EntityClassMixinDifference | CustomAttributeDifference; // @alpha -export type AnySchemaDifferenceConflict = SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict; +export type AnySchemaDifferenceConflict = SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict | SchemaDifferenceConflict; // @alpha export type AnySchemaEdits = SkipEdit | RenameSchemaItemEdit | RenamePropertyEdit; @@ -223,6 +223,8 @@ export enum ConflictCode { // (undocumented) ConflictingReferenceAlias = "C-002", // (undocumented) + ConflictingReferenceVersion = "C-003", + // (undocumented) ConstraintClassesDeriveFromAbstractConstraint = "C-1502", // (undocumented) DerivedConstraintsMustNarrowBaseConstraints = "C-1501",