diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index bc584f021..9d5cb97f3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -45,6 +45,7 @@ Stakeholders are by default represented with a dedicated graphical node connecte - https://github.com/eclipse-syson/syson/issues/2108[#2108] [diagrams] Leverage the latest change of the selection dialog to allow creating an `Actor` graphical node without specialization. - https://github.com/eclipse-syson/syson/issues/2111[#2111] [diagrams] Leverage the latest change of the selection dialog to allow creating a `Stakeholder` graphical node without specialization. - https://github.com/eclipse-syson/syson/issues/2122[#2122] [diagrams] Leverage the latest change of the selection dialog to allow creating a `Subject` graphical node without specialization. +- https://github.com/eclipse-syson/syson/issues/2129[#2129] [diagrams] Leverage the latest change of the selection dialog to allow creating a `FlowUsage` from a `ConnectionUsage` without selection a `PayloadFeature` === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFlowUsageTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFlowUsageTests.java index fb02abd4f..89005d5d9 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFlowUsageTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFlowUsageTests.java @@ -30,8 +30,6 @@ import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; import org.eclipse.sirius.components.collaborative.diagrams.dto.EditLabelInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.EditLabelSuccessPayload; -import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; -import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolSuccessPayload; import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariable; import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariableType; import org.eclipse.sirius.components.core.api.IEditingContext; @@ -43,7 +41,6 @@ import org.eclipse.sirius.components.diagrams.events.ReconnectEdgeKind; import org.eclipse.sirius.components.diagrams.tests.assertions.DiagramAssertions; import org.eclipse.sirius.components.diagrams.tests.graphql.EditLabelMutationRunner; -import org.eclipse.sirius.components.diagrams.tests.graphql.InvokeSingleClickOnDiagramElementToolMutationRunner; import org.eclipse.sirius.components.diagrams.tests.graphql.PaletteQueryRunner; import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload; @@ -112,9 +109,6 @@ public class GVFlowUsageTests extends AbstractIntegrationTests { @Autowired private EditLabelMutationRunner editLabelMutationRunner; - @Autowired - private InvokeSingleClickOnDiagramElementToolMutationRunner invokeSingleClickOnDiagramElementToolMutationRunner; - @Autowired private PaletteQueryRunner paletteQueryRunner; @@ -437,14 +431,7 @@ public void createFlowUsageInConnection() { Runnable createFlowUsageOnConnection = () -> { var selectedObjectVariable = new ToolVariable("selectedObject", GeneralViewFlowUsageProjectData.SemanticIds.VIDEO_SIGNAL_ID, ToolVariableType.OBJECT_ID); - var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, diagramId.get(), List.of(connectionEdgeId.get()), - flowCreationToolId, 0, 0, - List.of(selectedObjectVariable)); - var result = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(input); - String typename = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.__typename"); - assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName()); - List messages = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.messages[*].body"); - assertThat(messages).hasSize(0); + this.toolTester.invokeTool(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, diagramId.get(), connectionEdgeId.get(), flowCreationToolId, List.of(selectedObjectVariable)); }; Consumer validateEffectOnLabel = assertRefreshedDiagramThat(diagram -> this.assertEdgeLabelText(connectionEdgeId.get(), diagram, "cable : HDMICable \u25b6 Flow")); @@ -489,6 +476,85 @@ public void createFlowUsageInConnection() { .verify(Duration.ofSeconds(10)); } + @Test + @DisplayName("GIVEN a connection WHEN we create a flow usage in it without selecting a payload THEN the flow is correctly setup without a payload feature") + @GivenSysONServer({ GeneralViewFlowUsageProjectData.SCRIPT_PATH }) + public void createFlowUsageInConnectionWithoutPayloadFeature() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), + GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, + GeneralViewFlowUsageProjectData.GraphicalIds.DIAGRAM_ID); + + var flux = this.givenDiagramSubscription.subscribe(diagramEventInput); + + var diagramId = new AtomicReference(); + var connectionEdgeId = new AtomicReference(); + var connectionEdgeLabelId = new AtomicReference(); + + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + String flowCreationToolId = diagramDescriptionIdProvider.getNodeCreationToolIdOnEdge(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getConnectionUsage()), "New Flow"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + diagramId.set(diagram.getId()); + var connectionEdge = new DiagramNavigator(diagram).edgeWithId(GeneralViewFlowUsageProjectData.GraphicalIds.CONNECTION_EDGE_ID).getEdge(); + connectionEdgeId.set(connectionEdge.getId()); + connectionEdgeLabelId.set(connectionEdge.getCenterLabel().id()); + }); + + Runnable renameAndTypeTheConnection = () -> this.editLabel(diagramId.get(), connectionEdgeLabelId.get(), "cable : HDMICable"); + + Consumer validateLabelEditResult = assertRefreshedDiagramThat(diagram -> this.assertEdgeLabelText(connectionEdgeId.get(), diagram, "cable : HDMICable")); + + Runnable validateSemanticEffectOfLabelEdit = this.semanticRunnableFactory.createRunnable(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, + (editingContext, executeEditingContextFunctionInput) -> { + this.assertConnectionType(editingContext, GeneralViewFlowUsageProjectData.SemanticIds.CONNECT_ID, GeneralViewFlowUsageProjectData.SemanticIds.HDMI_CABLE_ID); + return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true); + }); + + Runnable createFlowUsageOnConnection = () -> { + var selectedObjectVariable = new ToolVariable("selectedObject", "", ToolVariableType.OBJECT_ID); + this.toolTester.invokeTool(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, diagramId.get(), connectionEdgeId.get(), flowCreationToolId, List.of(selectedObjectVariable)); + }; + + Consumer validateEffectOnLabel = assertRefreshedDiagramThat(diagram -> this.assertEdgeLabelText(connectionEdgeId.get(), diagram, "cable : HDMICable \u25b6 Flow")); + + Runnable validateSemanticEffectOfFlowCreation = this.semanticRunnableFactory.createRunnable(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, + (editingContext, executeEditingContextFunctionInput) -> { + var optionalConnection = this.objectSearchService.getObject(editingContext, GeneralViewFlowUsageProjectData.SemanticIds.CONNECT_ID); + assertThat(optionalConnection).containsInstanceOf(ConnectionUsage.class); + ConnectionUsage connection = (ConnectionUsage) optionalConnection.get(); + // The flow usage has been created + var optionalFlowUsage = connection.getOwnedFeature().stream().filter(FlowUsage.class::isInstance).map(FlowUsage.class::cast).findFirst(); + assertThat(optionalFlowUsage).isPresent(); + var flowUsage = optionalFlowUsage.get(); + + // The flow does not have a payload feature + var optionalPayloadFeature = flowUsage.getOwnedFeature().stream().filter(PayloadFeature.class::isInstance).map(PayloadFeature.class::cast).findFirst(); + assertThat(optionalPayloadFeature).isEmpty(); + + // The flow has two FlowEnds: one redefining HDMICable::inputSide, the other HDMICable::outputSide + var flowEnds = flowUsage.getOwnedFeature().stream().filter(FlowEnd.class::isInstance).map(FlowEnd.class::cast).toList(); + assertThat(flowEnds).hasSize(2); + var sourceEnd = flowEnds.get(0); + assertThat(sourceEnd.getOwnedFeature().get(0).getOwnedRedefinition().get(0).getRedefinedFeature().getQualifiedName()).isEqualTo("Package1::HDMICable::inputSide"); + var targetEnd = flowEnds.get(1); + assertThat(targetEnd.getOwnedFeature().get(0).getOwnedRedefinition().get(0).getRedefinedFeature().getQualifiedName()).isEqualTo("Package1::HDMICable::outputSide"); + return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(renameAndTypeTheConnection) + .consumeNextWith(validateLabelEditResult) + .then(validateSemanticEffectOfLabelEdit) + .then(createFlowUsageOnConnection) + .consumeNextWith(validateEffectOnLabel) + .then(validateSemanticEffectOfFlowCreation) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + @Test @DisplayName("GIVEN an untype connection WHEN we opening its palette THEN the flow tool is not proposed") @GivenSysONServer({ GeneralViewFlowUsageProjectData.SCRIPT_PATH }) diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/ToolTester.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/ToolTester.java index b43fe3e81..d3640494f 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/ToolTester.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/ToolTester.java @@ -77,7 +77,7 @@ public void invokeTool(String editingContextId, AtomicReference diagram } public void invokeTool(String editingContextId, String diagramId, String diagramElementId, String toolId, List variables) { - var createElementInput = new InvokeSingleClickOnDiagramElementToolInput( + var input = new InvokeSingleClickOnDiagramElementToolInput( UUID.randomUUID(), editingContextId, diagramId, @@ -86,9 +86,11 @@ public void invokeTool(String editingContextId, String diagramId, String diagram 0, 0, variables); - var createElementResult = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(createElementInput); - String typename = JsonPath.read(createElementResult.data(), "$.data.invokeSingleClickOnDiagramElementTool.__typename"); + var result = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.__typename"); assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName()); + List messages = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.messages[*].body"); + assertThat(messages).hasSize(0); } public void createNodeOnEdge(String editingContextId, AtomicReference diagram, String selectedEdgeTargetObjectLabel, String toolId) { diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationElementService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationElementService.java index daac73a99..32caa3730 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationElementService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationElementService.java @@ -615,14 +615,6 @@ public Connector reconnectTarget(Connector connector, Feature newTarget, Node so * the given {@link SatisfyRequirementUsage}. * @param newSource * the new source {@link Element}. - * @param sourceNode - * new source node of the edge - * @param targetNode - * target node of the edge - * @param editingContext - * the editing context - * @param diagram - * the context diagram * @return the given {@link SatisfyRequirementUsage}. */ public SatisfyRequirementUsage reconnectSatisfyRequirementSource(SatisfyRequirementUsage sru, Element newSource) { @@ -771,12 +763,14 @@ public FlowUsage createFlowUsage(Feature source, Feature target, Node sourceNode */ public FlowUsage createFlowUsageWithPayload(ConnectionUsage parent, Type payloadType) { var connectionTypes = parent.getType(); - if (connectionTypes.size() > 0 && connectionTypes.get(0).getOwnedEndFeature().size() >= 2) { - var connectionType = connectionTypes.get(0); + if (!connectionTypes.isEmpty() && connectionTypes.getFirst().getOwnedEndFeature().size() >= 2) { + var connectionType = connectionTypes.getFirst(); Feature source = connectionType.getOwnedEndFeature().get(0); Feature target = connectionType.getOwnedEndFeature().get(1); var flowUsage = this.metamodelMutationElementService.createFlowUsage(source, target, connectionType, connectionType, parent); - flowUsage.getOwnedRelationship().add(this.createPayloadFeatureMembership(payloadType)); + if (payloadType != null) { + flowUsage.getOwnedRelationship().add(this.createPayloadFeatureMembership(payloadType)); + } return flowUsage; } else { return null; diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/FlowNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/FlowNodeToolProvider.java index 9df155ebf..13ccbcff8 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/FlowNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/FlowNodeToolProvider.java @@ -71,8 +71,17 @@ private SelectionDialogDescription getSelectionDialogDescription() { return this.diagramBuilderHelper.newSelectionDialogDescription() .selectionDialogTreeDescription(selectionDialogTree) .defaultTitleExpression("New Flow") - .descriptionExpression("Select a payload for the new Flow:") - .optional(false) + .noSelectionTitleExpression("New Flow") + .withSelectionTitleExpression("New Flow") + .descriptionExpression("Create an Flow:") + .noSelectionActionLabelExpression("Create a new Flow") + .noSelectionActionDescriptionExpression("Create a new Flow without Payload") + .withSelectionActionLabelExpression("Select a Payload for the new Flow") + .withSelectionActionDescriptionExpression("Create a new Flow with a Payload") + .noSelectionActionStatusMessageExpression("It will create a new Flow without Payload") + .selectionRequiredWithoutSelectionStatusMessageExpression("Select one Element to be added as the new Flow Payload") + .selectionRequiredWithSelectionStatusMessageExpression(AQLConstants.AQL + "'It will create an Flow with ' + selectedObjects->first().name + ' as Payload'") + .optional(true) .build(); } 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 eb4a8ab65..40cf0b46b 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 @@ -35,6 +35,7 @@ image::release-notes-stakeholder-node.png[Default representation of Stakeholder ** Improve the tool to create an `Actor` graphical node by making specialization selection optional. ** Improve the tool to create a `Stakeholder` graphical node by making specialization selection optional. ** Improve the tool to create a `Subject` graphical node by making specialization selection optional. +** Improve the tool to create a `FlowUsage` from a `ConnectionUsage` by making the payload selection optional. == Technical details