From 5931a5d2509640cce58ee00d2aa2e8b69bc56982 Mon Sep 17 00:00:00 2001 From: Remona Date: Wed, 31 Dec 2025 11:10:41 +0530 Subject: [PATCH] fix: improve diagnostic path matching and add name uniqueness validation --- packages/react-model-ui/src/ModelContext.tsx | 5 +- .../language-server/cross-model-validator.ts | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/react-model-ui/src/ModelContext.tsx b/packages/react-model-ui/src/ModelContext.tsx index 25265115..6bfe4fa4 100644 --- a/packages/react-model-ui/src/ModelContext.tsx +++ b/packages/react-model-ui/src/ModelContext.tsx @@ -59,8 +59,11 @@ export class DiagnosticManager { // any ordering of index/property the server may emit. const propSuffix = `${ModelDiagnostic.ELEMENT_PROPERTY_SEPARATOR}${property}`; const idxMarker = `${ModelDiagnostic.ELEMENT_INDEX_SEPARATOR}${idx}`; + const altIdxMarker = `${ModelDiagnostic.ELEMENT_SEGMENT_SEPARATOR}${idx}`; const keys = Object.keys(this.result); - const candidateKey = keys.find(k => k.endsWith(propSuffix) && k.includes(idxMarker)); + const candidateKey = keys.find( + k => k.startsWith(rootElementPath) && k.endsWith(propSuffix) && (k.includes(idxMarker) || k.includes(altIdxMarker)) + ); if (candidateKey) { return this.result[candidateKey]; } diff --git a/packages/server/src/language-server/cross-model-validator.ts b/packages/server/src/language-server/cross-model-validator.ts index 8d486815..95114812 100644 --- a/packages/server/src/language-server/cross-model-validator.ts +++ b/packages/server/src/language-server/cross-model-validator.ts @@ -19,11 +19,15 @@ import { ID_PROPERTY, IdentifiableAstNode } from './cross-model-naming.js'; import { AttributeMapping, CrossModelAstType, + CustomProperty, IdentifiedObject, InheritanceEdge, isCrossModelRoot, + isCustomProperty, + isLogicalAttribute, isLogicalEntity, isMapping, + isRelationship, isSystemDiagram, LogicalAttribute, LogicalEntity, @@ -93,6 +97,57 @@ export class CrossModelValidator { property: 'name', data: { code: CrossModelValidationErrors.toMissing('name') } }); + return; + } + + const name = namedObject.name.toLowerCase(); + + if (isLogicalEntity(namedObject)) { + const root = namedObject.$container; + const other = root.entity; + if (other && other !== namedObject && other.name?.toLowerCase() === name) { + accept('error', `The entity name '${namedObject.name}' must be unique within the data model.`, { + node: namedObject, + property: 'name', + data: { code: CrossModelValidationErrors.toMalformed('name') } + }); + } + } + + if (isRelationship(namedObject)) { + const root = namedObject.$container; + const other = root.relationship; + if (other && other !== namedObject && other.name?.toLowerCase() === name) { + accept('error', `The relationship name '${namedObject.name}' must be unique within the data model.`, { + node: namedObject, + property: 'name', + data: { code: CrossModelValidationErrors.toMalformed('name') } + }); + } + } + + if (isLogicalAttribute(namedObject) && isLogicalEntity(namedObject.$container)) { + const entity = namedObject.$container; + const duplicates = entity.attributes.filter((a: LogicalAttribute) => a !== namedObject && a.name?.toLowerCase() === name); + if (duplicates.length > 0) { + accept('error', `The attribute name '${namedObject.name}' must be unique within the entity.`, { + node: namedObject, + property: 'name', + data: { code: CrossModelValidationErrors.toMalformed('name') } + }); + } + } + + if (isCustomProperty(namedObject)) { + const parent = namedObject.$container; + const duplicates = parent.customProperties.filter((p: CustomProperty) => p !== namedObject && p.name?.toLowerCase() === name); + if (duplicates.length > 0) { + accept('error', `The custom property name '${namedObject.name}' must be unique within the parent object.`, { + node: namedObject, + property: 'name', + data: { code: CrossModelValidationErrors.toMalformed('name') } + }); + } } }