From b0d41031f351f5e9504a12a79463af80e942f4e4 Mon Sep 17 00:00:00 2001 From: Pierre-Charles David Date: Mon, 4 May 2026 10:42:12 +0200 Subject: [PATCH] [2185] Fix ownership of constraints created inside requirement Bug: https://github.com/eclipse-syson/syson/issues/2185 Signed-off-by: Pierre-Charles David --- CHANGELOG.adoc | 1 + ...xplorerViewControllerIntegrationTests.java | 58 +++++++++++++++++++ ...etIntermediateContainerCreationSwitch.java | 18 +++++- .../pages/release-notes/2026.5.0.adoc | 6 +- 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d32824422..c6bc947a5 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -61,6 +61,7 @@ This change ensures compatibility with classpath resource resolution, which requ - https://github.com/eclipse-syson/syson/issues/2045[#2045] [diagrams] In Interconnection View diagrams, fix an issue where the `parts` compartment of a `PartDefinition` graphical node was incorrectly revealed when creating a `PartUsage` from the `PartDefinition` graphical node, even if the `interconnection` compartment was already visible. - https://github.com/eclipse-syson/syson/issues/2094[#2094] [libraries] Ensure library documents are read-only. - https://github.com/eclipse-syson/syson/issues/2143[#2143] [configuration] Fixed classpath resource resolution on Windows by enforcing forward slashes (`/`) in library paths. +- https://github.com/eclipse-syson/syson/issues/2185[#2185] [explorer] When creating a `ConstraintUsage` inside a `Requirement` the new constraint is now correctly owned through a `RequirementConstraintMembership`. === Improvements diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java index 2c338e07e..f0b723e95 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -34,6 +35,8 @@ import org.eclipse.sirius.components.collaborative.dto.CreateChildSuccessPayload; import org.eclipse.sirius.components.core.api.ErrorPayload; import org.eclipse.sirius.components.core.api.IIdentityService; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionInput; import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload; @@ -57,9 +60,11 @@ import org.eclipse.syson.application.data.GeneralViewEmptyTestProjectData; import org.eclipse.syson.application.data.ProjectWithLibraryDependencyContainingLibraryPackageTestProjectData; import org.eclipse.syson.application.data.WithUserLibrariesTestProjectData; +import org.eclipse.syson.sysml.ConstraintUsage; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.LibraryPackage; import org.eclipse.syson.sysml.Namespace; +import org.eclipse.syson.sysml.RequirementConstraintMembership; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.tree.explorer.filters.SysONTreeFilterConstants; import org.eclipse.syson.tree.explorer.fragments.LibrariesDirectory; @@ -73,6 +78,7 @@ import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; /** @@ -136,6 +142,9 @@ private record TreeItemMatcher(Function treeItemFinder, Predicat @Autowired private IIdentityService identityService; + @Autowired + private IObjectSearchService objectSearchService; + @Autowired private ExplorerEventSubscriptionRunner treeEventSubscriptionRunner; @@ -407,6 +416,55 @@ public void testSysONFiltersOnSiriusExplorer() { assertThat(treeFilterIds).isEmpty(); } + @DisplayName("GIVEN the Sirius Explorer View, WHEN creating a constraint inside a requirement, THEN the new constraint is owned through a RequirementConstraintMembership") + @GivenSysONServer({ ExplorerViewDirectEditTestProjectData.SCRIPT_PATH }) + @Test + public void testCreateConstraintInRequirement() { + var expandedIds = this.getAllTreeItemIds(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID); + var activatedFilters = List.of(SysONTreeFilterConstants.HIDE_ROOT_NAMESPACES_ID); + var treeRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONTreeViewDescriptionProvider.getDescriptionId(), expandedIds, activatedFilters); + + var treeEventInput = new ExplorerEventInput(UUID.randomUUID(), ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId); + var treeFlux = this.treeEventSubscriptionRunner.run(treeEventInput).flux(); + + var newConstraintId = new AtomicReference(null); + + Consumer ignorePayload = (o) -> { + // Ignore the refresh event payload, we will check the actual semantic model content. + }; + + Runnable createChildConstraint = () -> { + var input = new CreateChildInput(UUID.randomUUID(), ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, ExplorerViewDirectEditTestProjectData.SemanticIds.REQ1_RU_ID, + "SysMLv2EditService-ConstraintUsage"); + var result = this.createChildMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.createChild.__typename"); + assertThat(typename).isEqualTo(CreateChildSuccessPayload.class.getSimpleName()); + String objectId = JsonPath.read(result.data(), "$.data.createChild.object.id"); + newConstraintId.set(objectId); + }; + + Runnable checkConstraintOwnership = () -> { + var editingContextFunctionInput = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, (editingContext, input) -> { + var optionalContraint = this.objectSearchService.getObject(editingContext, newConstraintId.get()); + assertThat(optionalContraint).containsInstanceOf(ConstraintUsage.class); + var constraint = (ConstraintUsage) optionalContraint.get(); + assertThat(constraint.getOwningRelationship()).isInstanceOf(RequirementConstraintMembership.class); + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalContraint.get()); + }); + Mono result = this.executeEditingContextFunctionRunner.execute(editingContextFunctionInput); + var payload = result.block(); + assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class); + }; + + StepVerifier.create(treeFlux) + .consumeNextWith(ignorePayload) + .then(createChildConstraint) + .consumeNextWith(ignorePayload) + .then(checkConstraintOwnership) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + private List getAllTreeItemIds(String editingContextId) { ExecuteEditingContextFunctionInput executeEditingContextFunctionInput = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), editingContextId, (editingContext, input) -> { if (editingContext instanceof IEMFEditingContext emfEditingContext) { diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/GetIntermediateContainerCreationSwitch.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/GetIntermediateContainerCreationSwitch.java index d6fa9f254..93d25092b 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/GetIntermediateContainerCreationSwitch.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/GetIntermediateContainerCreationSwitch.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -18,6 +18,7 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.syson.sysml.Comment; +import org.eclipse.syson.sysml.ConstraintUsage; import org.eclipse.syson.sysml.Definition; import org.eclipse.syson.sysml.Documentation; import org.eclipse.syson.sysml.Element; @@ -27,6 +28,8 @@ import org.eclipse.syson.sysml.Membership; import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.Package; +import org.eclipse.syson.sysml.RequirementDefinition; +import org.eclipse.syson.sysml.RequirementUsage; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysml.TextualRepresentation; import org.eclipse.syson.sysml.Usage; @@ -54,6 +57,19 @@ public Optional caseComment(Comment object) { return Optional.of(SysmlPackage.eINSTANCE.getOwningMembership()); } + @Override + public Optional caseConstraintUsage(ConstraintUsage object) { + Optional intermediateContainer; + if (this.container instanceof RequirementDefinition || this.container instanceof RequirementUsage) { + // SysML v2 8.3.21.7: "A RequirementConstraintMembership is a FeatureMembership for an assumed or required + // ConstraintUsage of a RequirementDefinition or RequirementUsage." + intermediateContainer = Optional.of(SysmlPackage.eINSTANCE.getRequirementConstraintMembership()); + } else { + intermediateContainer = this.caseFeature(object); + } + return intermediateContainer; + } + @Override public Optional caseDefinition(Definition object) { return Optional.of(SysmlPackage.eINSTANCE.getOwningMembership()); diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc index 2c0eca061..3860d2d6a 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc @@ -16,7 +16,11 @@ * In _Details_ view: -** Fix an issue that allowed to edit library objects via the _Details_ view. +** Fix an issue that allowed to edit library objects via the _Details_ view. + +* In the _Explorer_ view: + +** When creating a `ConstraintUsage` inside a `Requirement` the new constraint is now correctly owned through a `RequirementConstraintMembership`. == Improvements