diff --git a/common/api/ecschema-metadata.api.md b/common/api/ecschema-metadata.api.md index 89e85f104720..0e41532625be 100644 --- a/common/api/ecschema-metadata.api.md +++ b/common/api/ecschema-metadata.api.md @@ -1525,6 +1525,10 @@ export interface RelationshipConstraintProps { // (undocumented) readonly constraintClasses: string[]; // (undocumented) + readonly customAttributes?: Array<{ + [value: string]: any; + }>; + // (undocumented) readonly multiplicity: string; // (undocumented) readonly polymorphic: boolean; diff --git a/common/changes/@itwin/ecschema-metadata/constraint-customattributes-prop_2024-12-20-15-39.json b/common/changes/@itwin/ecschema-metadata/constraint-customattributes-prop_2024-12-20-15-39.json new file mode 100644 index 000000000000..5dd8c0e532c0 --- /dev/null +++ b/common/changes/@itwin/ecschema-metadata/constraint-customattributes-prop_2024-12-20-15-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/ecschema-metadata", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/ecschema-metadata" +} \ No newline at end of file diff --git a/core/ecschema-metadata/src/Deserialization/JsonProps.ts b/core/ecschema-metadata/src/Deserialization/JsonProps.ts index 2e4404ba7796..c003764a3a81 100644 --- a/core/ecschema-metadata/src/Deserialization/JsonProps.ts +++ b/core/ecschema-metadata/src/Deserialization/JsonProps.ts @@ -136,6 +136,7 @@ export interface RelationshipConstraintProps { readonly polymorphic: boolean; readonly abstractConstraint?: string; readonly constraintClasses: string[]; + readonly customAttributes?: Array<{ [value: string]: any }>; } /** diff --git a/core/ecschema-metadata/src/test/Metadata/RelationshipConstraint.test.ts b/core/ecschema-metadata/src/test/Metadata/RelationshipConstraint.test.ts index d2399c46e2fd..daa4a5e076fb 100644 --- a/core/ecschema-metadata/src/test/Metadata/RelationshipConstraint.test.ts +++ b/core/ecschema-metadata/src/test/Metadata/RelationshipConstraint.test.ts @@ -9,6 +9,7 @@ import { ECObjectsError } from "../../Exception"; import { RelationshipClass, RelationshipConstraint } from "../../Metadata/RelationshipClass"; import { Schema } from "../../Metadata/Schema"; import { createSchemaJsonWithItems } from "../TestUtils/DeserializationHelpers"; +import { createEmptyXmlDocument, getElementChildren, getElementChildrenByTagName } from "../TestUtils/SerializationHelper"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -39,9 +40,24 @@ function createSchemaJson(sourceConst: any, targetConst: any) { schemaItemType: "EntityClass", baseClass: "TestSchema.TargetBaseEntity", }, - TestCAClassA: { schemaItemType: "CustomAttributeClass", appliesTo: "Any" }, - TestCAClassB: { schemaItemType: "CustomAttributeClass", appliesTo: "Any" }, - TestCAClassC: { schemaItemType: "CustomAttributeClass", appliesTo: "Any" }, + TestCAClassA: { schemaItemType: "CustomAttributeClass", appliesTo: "Any", properties: [ + { + name: "ShowClasses", + type: "PrimitiveProperty", + typeName: "boolean", + }]}, + TestCAClassB: { schemaItemType: "CustomAttributeClass", appliesTo: "Any", properties: [ + { + name: "ShowClasses", + type: "PrimitiveProperty", + typeName: "boolean", + }]}, + TestCAClassC: { schemaItemType: "CustomAttributeClass", appliesTo: "Any", properties: [ + { + name: "ShowClasses", + type: "PrimitiveProperty", + typeName: "boolean", + }] }, }); } @@ -159,4 +175,206 @@ describe("RelationshipConstraint", () => { assert.isTrue(testConstraint.customAttributes!.get("TestSchema.TestCAClassB")!.ShowClasses); }); }); + + describe("toJson", () => { + let testConstraint: RelationshipConstraint; + + beforeEach(() => { + const schema = new Schema(new SchemaContext(), "TestSchema", "ts", 1, 0, 0); + const relClass = new RelationshipClass(schema, "TestRelationship"); + testConstraint = new RelationshipConstraint(relClass, RelationshipEnd.Source); + }); + + const targetStubJson = { + polymorphic: false, + multiplicity: "(0..*)", + roleLabel: "Test Target roleLabel", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + }; + + const oneCustomAttributeJson = { + polymorphic: true, + multiplicity: "(0..1)", + roleLabel: "Test Source roleLabel", + constraintClasses: [ + "TestSchema.TestSourceEntity", + ], + customAttributes: [ + { + className: "TestSchema.TestCAClassA", + ShowClasses: true, + }, + ], + }; + + it("Serialize One Custom Attribute", async () => { + const schema = await Schema.fromJson(createSchemaJson(oneCustomAttributeJson, targetStubJson), new SchemaContext()); + testConstraint = (await schema.getItem("TestRelationship"))!.source; + expect(testConstraint).to.exist; + const constraintProps = testConstraint.toJSON(); + assert.isTrue(constraintProps.customAttributes![0].ShowClasses); + }); + + const twoCustomAttributesJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "ValidSchema", + polymorphic: true, + multiplicity: "(0..1)", + roleLabel: "Test Source roleLabel", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + customAttributes: [ + { + className: "TestSchema.TestCAClassA", + }, + { + className: "TestSchema.TestCAClassB", + }, + ], + }; + + it("Serialize Two Custom Attributes", async () => { + const schema = await Schema.fromJson(createSchemaJson(twoCustomAttributesJson, targetStubJson), new SchemaContext()); + testConstraint = (await schema.getItem("TestRelationship"))!.source; + expect(testConstraint).to.exist; + const constraintProps = testConstraint.toJSON(); + expect(constraintProps.customAttributes![0].className).to.equal("TestSchema.TestCAClassA"); + expect(constraintProps.customAttributes![1].className).to.equal("TestSchema.TestCAClassB"); + }); + + it("Serialize Two Custom Attributes with additional properties", () => { + const relConstraintJson = { + polymorphic: true, + multiplicity: "(0..1)", + roleLabel: "test roleLabel", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + customAttributes: [ + { + className: "TestSchema.TestCAClassA", + ShowClasses: false, + }, + { + className: "TestSchema.TestCAClassB", + ShowClasses: true, + }, + ], + }; + const schema = Schema.fromJsonSync(createSchemaJson(relConstraintJson, targetStubJson), new SchemaContext()); + testConstraint = schema.getItemSync("TestRelationship")!.source; + expect(testConstraint).to.exist; + const constraintProps = testConstraint.toJSON(); + expect(constraintProps.customAttributes![0].ShowClasses).to.be.false; + expect(constraintProps.customAttributes![1].ShowClasses).to.be.true; + }); + }); + + describe("toXML", () => { + const newDom = createEmptyXmlDocument(); + let testConstraint: RelationshipConstraint; + + beforeEach(() => { + const schema = new Schema(new SchemaContext(), "TestSchema", "ts", 1, 0, 0); + const relClass = new RelationshipClass(schema, "TestRelationship"); + testConstraint = new RelationshipConstraint(relClass, RelationshipEnd.Source); + }); + + const targetStubJson = { + polymorphic: false, + multiplicity: "(0..*)", + roleLabel: "Test Target roleLabel", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + }; + + const oneCustomAttributeJson = { + polymorphic: true, + multiplicity: "(0..1)", + roleLabel: "Test Source roleLabel", + constraintClasses: [ + "TestSchema.TestSourceEntity", + ], + customAttributes: [ + { + className: "TestSchema.TestCAClassA", + ShowClasses: true, + }, + ], + }; + + it("Serialize One Custom Attribute", async () => { + const schema = await Schema.fromJson(createSchemaJson(oneCustomAttributeJson, targetStubJson), new SchemaContext()); + testConstraint = (await schema.getItem("TestRelationship"))!.source; + expect(testConstraint).to.exist; + const serialized = await testConstraint.toXml(newDom); + expect(serialized.nodeName).to.eql("Source"); + expect(serialized.getAttribute("polymorphic")).to.eql("true"); + expect(serialized.getAttribute("multiplicity")).to.eql("(0..1)"); + expect(serialized.getAttribute("roleLabel")).to.eql("Test Source roleLabel"); + const children = getElementChildren(serialized); + assert.strictEqual(children.length, 2); + + const customAttributes = getElementChildrenByTagName(serialized, "ECCustomAttributes"); + assert.strictEqual(customAttributes.length, 1); + const customAttribute = getElementChildrenByTagName(customAttributes[0], "TestCAClassA"); + assert.strictEqual(customAttribute.length, 1); + const attribute = getElementChildrenByTagName(customAttribute[0], "ShowClasses"); + assert.strictEqual(attribute.length, 1); + expect(attribute[0].textContent).to.eql("True"); + }); + + const twoCustomAttributesJson = { + $schema: "https://dev.bentley.com/json_schemas/ec/32/ecschema", + name: "ValidSchema", + polymorphic: true, + multiplicity: "(0..1)", + roleLabel: "Test Source roleLabel", + constraintClasses: [ + "TestSchema.TestTargetEntity", + ], + customAttributes: [ + { + className: "TestSchema.TestCAClassA", + ShowClasses: true, + }, + { + className: "TestSchema.TestCAClassB", + ShowClasses: true, + }, + ], + }; + + it("Serialize Two Custom Attributes", async () => { + const schema = await Schema.fromJson(createSchemaJson(twoCustomAttributesJson, targetStubJson), new SchemaContext()); + testConstraint = (await schema.getItem("TestRelationship"))!.source; + expect(testConstraint).to.exist; + const serialized = await testConstraint.toXml(newDom); + + expect(serialized.nodeName).to.eql("Source"); + expect(serialized.getAttribute("polymorphic")).to.eql("true"); + expect(serialized.getAttribute("multiplicity")).to.eql("(0..1)"); + expect(serialized.getAttribute("roleLabel")).to.eql("Test Source roleLabel"); + const children = getElementChildren(serialized); + assert.strictEqual(children.length, 2); + + const customAttributes = getElementChildrenByTagName(serialized, "ECCustomAttributes"); + assert.strictEqual(customAttributes.length, 1); + const customAttribute1 = getElementChildrenByTagName(customAttributes[0], "TestCAClassA"); + assert.strictEqual(customAttribute1.length, 1); + const attribute1 = getElementChildrenByTagName(customAttribute1[0], "ShowClasses"); + assert.strictEqual(attribute1.length, 1); + expect(attribute1[0].textContent).to.eql("True"); + + const customAttribute2 = getElementChildrenByTagName(customAttributes[0], "TestCAClassB"); + assert.strictEqual(customAttribute2.length, 1); + const attribute2 = getElementChildrenByTagName(customAttribute2[0], "ShowClasses"); + assert.strictEqual(attribute2.length, 1); + expect(attribute2[0].textContent).to.eql("True"); + }); + }); });