Skip to content

Commit 27cb539

Browse files
pcdavidAxelRICHARD
authored andcommitted
[2013] Add a tool to create and setup a FlowUsage on a ConnectionUsage
Bug: #2013 Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
1 parent 78ace55 commit 27cb539

File tree

15 files changed

+549
-5
lines changed

15 files changed

+549
-5
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ All validation rules have been updated to comply with the SysMLv2 2025-04 specif
623623

624624
- https://github.com/eclipse-syson/syson/issues/1396[#1396] [general-view] Add a graphical edge representation for `IncludeUseCaseUsage` in the _General View_ diagram.
625625
- https://github.com/eclipse-syson/syson/issues/1405[#1405] [export] Implement the textual export of `FlowUsage`.
626+
- https://github.com/eclipse-syson/syson/issues/2013[#2013] [diagram] Add a tool to create and setup a `FlowUsage` on a `ConnectionUsage` graphical edge.
626627

627628
== v2025.6.0
628629

backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFlowUsageTests.java

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,36 @@
1717
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramAssertions.assertThat;
1818
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE_STYLE;
1919

20+
import com.jayway.jsonpath.JsonPath;
21+
2022
import java.time.Duration;
23+
import java.util.List;
24+
import java.util.Map;
2125
import java.util.UUID;
2226
import java.util.concurrent.atomic.AtomicReference;
2327
import java.util.function.Consumer;
2428

2529
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput;
2630
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload;
31+
import org.eclipse.sirius.components.collaborative.diagrams.dto.EditLabelInput;
32+
import org.eclipse.sirius.components.collaborative.diagrams.dto.EditLabelSuccessPayload;
33+
import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput;
34+
import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolSuccessPayload;
35+
import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariable;
36+
import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariableType;
37+
import org.eclipse.sirius.components.core.api.IEditingContext;
2738
import org.eclipse.sirius.components.core.api.IIdentityService;
2839
import org.eclipse.sirius.components.core.api.IObjectSearchService;
2940
import org.eclipse.sirius.components.diagrams.ArrowStyle;
3041
import org.eclipse.sirius.components.diagrams.Diagram;
3142
import org.eclipse.sirius.components.diagrams.Edge;
3243
import org.eclipse.sirius.components.diagrams.events.ReconnectEdgeKind;
3344
import org.eclipse.sirius.components.diagrams.tests.assertions.DiagramAssertions;
45+
import org.eclipse.sirius.components.diagrams.tests.graphql.EditLabelMutationRunner;
46+
import org.eclipse.sirius.components.diagrams.tests.graphql.InvokeSingleClickOnDiagramElementToolMutationRunner;
47+
import org.eclipse.sirius.components.diagrams.tests.graphql.PaletteQueryRunner;
48+
import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator;
49+
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload;
3450
import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider;
3551
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
3652
import org.eclipse.syson.AbstractIntegrationTests;
@@ -40,13 +56,18 @@
4056
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeCreationTester;
4157
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeReconnectionTester;
4258
import org.eclipse.syson.application.data.GeneralViewFlowConnectionItemUsagesProjectData;
59+
import org.eclipse.syson.application.data.GeneralViewFlowUsageProjectData;
4360
import org.eclipse.syson.services.SemanticRunnableFactory;
4461
import org.eclipse.syson.services.diagrams.DiagramComparator;
4562
import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider;
4663
import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription;
4764
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
4865
import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator;
66+
import org.eclipse.syson.sysml.ConnectionUsage;
67+
import org.eclipse.syson.sysml.FeatureTyping;
68+
import org.eclipse.syson.sysml.FlowEnd;
4969
import org.eclipse.syson.sysml.FlowUsage;
70+
import org.eclipse.syson.sysml.PayloadFeature;
5071
import org.eclipse.syson.sysml.SysmlPackage;
5172
import org.eclipse.syson.util.IDescriptionNameGenerator;
5273
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
@@ -86,6 +107,15 @@ public class GVFlowUsageTests extends AbstractIntegrationTests {
86107
@Autowired
87108
private EdgeCreationTester edgeCreationTester;
88109

110+
@Autowired
111+
private EditLabelMutationRunner editLabelMutationRunner;
112+
113+
@Autowired
114+
private InvokeSingleClickOnDiagramElementToolMutationRunner invokeSingleClickOnDiagramElementToolMutationRunner;
115+
116+
@Autowired
117+
private PaletteQueryRunner paletteQueryRunner;
118+
89119
@Autowired
90120
private DiagramComparator diagramComparator;
91121

@@ -297,4 +327,163 @@ public void reconnectFlowUsageSource() {
297327
.verify(Duration.ofSeconds(10));
298328
}
299329

330+
@Test
331+
@DisplayName("GIVEN a connection WHEN we create a flow usage in it THEN the flow is correctly setup")
332+
@GivenSysONServer({ GeneralViewFlowUsageProjectData.SCRIPT_PATH })
333+
public void createFlowUsageInConnection() {
334+
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(),
335+
GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
336+
GeneralViewFlowUsageProjectData.GraphicalIds.DIAGRAM_ID);
337+
338+
var flux = this.givenDiagramSubscription.subscribe(diagramEventInput);
339+
340+
var diagramId = new AtomicReference<String>();
341+
var connectionEdgeId = new AtomicReference<String>();
342+
var connectionEdgeLabelId = new AtomicReference<String>();
343+
344+
var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
345+
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
346+
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);
347+
String flowCreationToolId = diagramDescriptionIdProvider.getNodeCreationToolIdOnEdge(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getConnectionUsage()), "New Flow");
348+
349+
Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> {
350+
diagramId.set(diagram.getId());
351+
var connectionEdge = new DiagramNavigator(diagram).edgeWithId(GeneralViewFlowUsageProjectData.GraphicalIds.CONNECTION_EDGE_ID).getEdge();
352+
connectionEdgeId.set(connectionEdge.getId());
353+
connectionEdgeLabelId.set(connectionEdge.getCenterLabel().id());
354+
});
355+
356+
Runnable renameAndTypeTheConnection = () -> this.editLabel(diagramId.get(), connectionEdgeLabelId.get(), "cable : HDMICable");
357+
358+
Consumer<Object> validateLabelEditResult = assertRefreshedDiagramThat(diagram -> this.assertEdgeLabelText(connectionEdgeId.get(), diagram, "cable : HDMICable"));
359+
360+
Runnable validateSemanticEffectOfLabelEdit = this.semanticRunnableFactory.createRunnable(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
361+
(editingContext, executeEditingContextFunctionInput) -> {
362+
this.assertConnectionType(editingContext, GeneralViewFlowUsageProjectData.SemanticIds.CONNECT_ID, GeneralViewFlowUsageProjectData.SemanticIds.HDMI_CABLE_ID);
363+
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
364+
});
365+
366+
Runnable createFlowUsageOnConnection = () -> {
367+
var selectedObjectVariable = new ToolVariable("selectedObject", GeneralViewFlowUsageProjectData.SemanticIds.VIDEO_SIGNAL_ID, ToolVariableType.OBJECT_ID);
368+
var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, diagramId.get(), List.of(connectionEdgeId.get()),
369+
flowCreationToolId, 0, 0,
370+
List.of(selectedObjectVariable));
371+
var result = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(input);
372+
String typename = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.__typename");
373+
assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName());
374+
List<String> messages = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.messages[*].body");
375+
assertThat(messages).hasSize(0);
376+
};
377+
378+
Consumer<Object> validateEffectOnLabel = assertRefreshedDiagramThat(diagram -> this.assertEdgeLabelText(connectionEdgeId.get(), diagram, "cable : HDMICable \u25b6 Flow"));
379+
380+
Runnable validateSemanticEffectOfFlowCreation = this.semanticRunnableFactory.createRunnable(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
381+
(editingContext, executeEditingContextFunctionInput) -> {
382+
var optionalConnection = this.objectSearchService.getObject(editingContext, GeneralViewFlowUsageProjectData.SemanticIds.CONNECT_ID);
383+
assertThat(optionalConnection).containsInstanceOf(ConnectionUsage.class);
384+
ConnectionUsage connection = (ConnectionUsage) optionalConnection.get();
385+
// The flow usage has been created
386+
var optionalFlowUsage = connection.getOwnedFeature().stream().filter(FlowUsage.class::isInstance).map(FlowUsage.class::cast).findFirst();
387+
assertThat(optionalFlowUsage).isPresent();
388+
var flowUsage = optionalFlowUsage.get();
389+
390+
// The flow has a FeatureMembership with a PayloadFeature typed by "Video Signal" (the passed
391+
// payload)
392+
var optionalPayloadFeature = flowUsage.getOwnedFeature().stream().filter(PayloadFeature.class::isInstance).map(PayloadFeature.class::cast).findFirst();
393+
assertThat(optionalPayloadFeature).isPresent();
394+
var payloadFeature = optionalPayloadFeature.get();
395+
var payloadType = ((FeatureTyping) payloadFeature.getOwnedRelationship().get(0)).getType();
396+
assertThat(this.identityService.getId(payloadType)).isEqualTo(GeneralViewFlowUsageProjectData.SemanticIds.VIDEO_SIGNAL_ID);
397+
398+
// The flow has two FlowEnds: one redefining HDMICable::inputSide, the other HDMICable::outputSide
399+
var flowEnds = flowUsage.getOwnedFeature().stream().filter(FlowEnd.class::isInstance).map(FlowEnd.class::cast).toList();
400+
assertThat(flowEnds).hasSize(2);
401+
var sourceEnd = flowEnds.get(0);
402+
assertThat(sourceEnd.getOwnedFeature().get(0).getOwnedRedefinition().get(0).getRedefinedFeature().getQualifiedName()).isEqualTo("Package1::HDMICable::inputSide");
403+
var targetEnd = flowEnds.get(1);
404+
assertThat(targetEnd.getOwnedFeature().get(0).getOwnedRedefinition().get(0).getRedefinedFeature().getQualifiedName()).isEqualTo("Package1::HDMICable::outputSide");
405+
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
406+
});
407+
408+
StepVerifier.create(flux)
409+
.consumeNextWith(initialDiagramContentConsumer)
410+
.then(renameAndTypeTheConnection)
411+
.consumeNextWith(validateLabelEditResult)
412+
.then(validateSemanticEffectOfLabelEdit)
413+
.then(createFlowUsageOnConnection)
414+
.consumeNextWith(validateEffectOnLabel)
415+
.then(validateSemanticEffectOfFlowCreation)
416+
.thenCancel()
417+
.verify(Duration.ofSeconds(10));
418+
}
419+
420+
@Test
421+
@DisplayName("GIVEN an untype connection WHEN we opening its palette THEN the flow tool is not proposed")
422+
@GivenSysONServer({ GeneralViewFlowUsageProjectData.SCRIPT_PATH })
423+
public void createFlowUsageInUntypedConnectionConnection() {
424+
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(),
425+
GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
426+
GeneralViewFlowUsageProjectData.GraphicalIds.DIAGRAM_ID);
427+
428+
var flux = this.givenDiagramSubscription.subscribe(diagramEventInput);
429+
430+
var diagramId = new AtomicReference<String>();
431+
var connectionEdgeId = new AtomicReference<String>();
432+
433+
var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
434+
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
435+
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);
436+
String flowCreationToolId = diagramDescriptionIdProvider.getNodeCreationToolIdOnEdge(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getConnectionUsage()), "New Flow");
437+
438+
Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> {
439+
diagramId.set(diagram.getId());
440+
var connectionEdge = new DiagramNavigator(diagram).edgeWithId(GeneralViewFlowUsageProjectData.GraphicalIds.CONNECTION_EDGE_ID).getEdge();
441+
connectionEdgeId.set(connectionEdge.getId());
442+
});
443+
444+
Runnable getEdgePalette = () -> {
445+
Map<String, Object> variables = Map.of(
446+
"editingContextId", GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID,
447+
"representationId", diagramId.get(),
448+
"diagramElementIds", List.of(connectionEdgeId.get()));
449+
var result = this.paletteQueryRunner.run(variables);
450+
Map<String, Object> behaviorSection = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[1]");
451+
assertThat(behaviorSection.get("label")).isEqualTo("Behavior");
452+
List<Object> behaviorTools = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[1].tools");
453+
assertThat(behaviorTools).isEmpty();
454+
List<String> allTools = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[*].tools[*].id");
455+
assertThat(allTools).doesNotContain(flowCreationToolId);
456+
};
457+
458+
459+
StepVerifier.create(flux)
460+
.consumeNextWith(initialDiagramContentConsumer)
461+
.then(getEdgePalette)
462+
.thenCancel()
463+
.verify(Duration.ofSeconds(10));
464+
}
465+
466+
private void editLabel(String diagramId, String labelId, String newText) {
467+
var input = new EditLabelInput(UUID.randomUUID(), GeneralViewFlowUsageProjectData.EDITING_CONTEXT_ID, diagramId, labelId, newText);
468+
var result = this.editLabelMutationRunner.run(input);
469+
String typename = JsonPath.read(result.data(), "$.data.editLabel.__typename");
470+
assertThat(typename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName());
471+
List<String> messages = JsonPath.read(result.data(), "$.data.editLabel.messages[*].body");
472+
assertThat(messages).hasSize(0);
473+
}
474+
475+
private void assertEdgeLabelText(String connectionEdgeId, Diagram diagram, String expectedLabelText) {
476+
var edge = new DiagramNavigator(diagram).edgeWithId(connectionEdgeId).getEdge();
477+
DiagramAssertions.assertThat(edge.getCenterLabel()).hasText(expectedLabelText);
478+
}
479+
480+
private void assertConnectionType(IEditingContext editingContext, String connectionElementId, String expectedTypeElementId) {
481+
var optionalConnection = this.objectSearchService.getObject(editingContext, connectionElementId);
482+
assertThat(optionalConnection).containsInstanceOf(ConnectionUsage.class);
483+
ConnectionUsage connection = (ConnectionUsage) optionalConnection.get();
484+
assertThat(connection.getType()).hasSize(1);
485+
var connectionType = connection.getType().get(0);
486+
var connectionTypeId = this.identityService.getId(connectionType);
487+
assertThat(connectionTypeId).isEqualTo(expectedTypeElementId);
488+
}
300489
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Obeo.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Obeo - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.syson.application.data;
14+
15+
/**
16+
* Ids for project "GeneralView-FlowUsage".
17+
*
18+
* @author pcdavid
19+
*/
20+
public class GeneralViewFlowUsageProjectData {
21+
public static final String EDITING_CONTEXT_ID = "4b2c5775-0728-4b12-a520-89a8f2b90041";
22+
23+
public static final String SCRIPT_PATH = "/scripts/database-content/GeneralView-FlowUsage.sql";
24+
25+
/**
26+
* Ids of the graphical elements elements.
27+
*/
28+
public static final class GraphicalIds {
29+
public static final String DIAGRAM_ID = "2a1b62cf-36ea-41d2-8433-8ff781b3f4e5";
30+
31+
public static final String CONNECTION_EDGE_ID = "7f3367d5-a200-339b-820a-2408e5b82c84";
32+
}
33+
34+
/**
35+
* Ids of the semantic elements.
36+
*/
37+
public static final class SemanticIds {
38+
public static final String CONNECT_ID = "1604e1d8-1e30-44fb-a147-9f50f660f803";
39+
40+
public static final String HDMI_CABLE_ID = "1df3f0e9-7d6d-4dd6-93d9-206129fd793f";
41+
42+
public static final String VIDEO_SIGNAL_ID = "d9b2dd49-4bae-44a7-8137-73bcaf0a2d5e";
43+
44+
}
45+
}

backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewPartUsageRedefinitionProjectData.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
package org.eclipse.syson.application.data;
1414

1515
/**
16-
* Ids for project "GeneralView-GeneralView-PartUsage-redefinition".
16+
* Ids for project "GeneralView-PartUsage-redefinition".
1717
*
1818
* @author pcdavid
1919
*/

0 commit comments

Comments
 (0)