Skip to content

Commit a4f7d79

Browse files
theogiraudetsbegaudeau
authored andcommitted
[5062] Add new Views Explorer view
Bug: #5062 Signed-off-by: Théo Giraudet <theo.giraudet@obeo.fr>
1 parent fa599fc commit a4f7d79

File tree

34 files changed

+1515
-7
lines changed

34 files changed

+1515
-7
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ For example, the tools contained in a given `NodeDescription` can only be trigge
9292
The key bindings are visible in the palette next to the tool name.
9393
A key binding needs to contain at least one of `Control`, `Alt`, or `Meta` to be executed by the frontend.
9494
- https://github.com/eclipse-sirius/sirius-web/issues/3488[#3488] [diagram] Restore auto-layout support for diagrams
95+
- https://github.com/eclipse-sirius/sirius-web/issues/5062[#5062] [sirius-web] Add new "views explorer" view
9596

9697

9798
=== Improvements

integration-tests-cypress/cypress/e2e/project/workbench/workbench-configuration.cy.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ describe('Workbench Configuration Resolution', () => {
114114
workbench = new Workbench();
115115
});
116116

117-
it('Then, the left panel is collapsed and all views ("Explorer", "Validation") are not highlighted and not active', () => {
117+
it('Then, the left panel is collapsed and all views ("Explorer", "Validation", Views) are not highlighted and not active', () => {
118118
workbench.checkPanelState('left', 'collapsed');
119119
workbench.isIconHighlighted('left', 'Explorer', false);
120120
workbench.isIconHighlighted('left', 'Validation', false);
121+
workbench.isIconHighlighted('left', 'Views', false);
121122
cy.getByTestId('site-left').should('not.exist');
122123
});
123124

@@ -150,11 +151,12 @@ describe('Workbench Configuration Resolution', () => {
150151
workbench = new Workbench();
151152
});
152153

153-
it('Then, the left panel is expanded and all views ("Explorer", "Validation") are highlighted and active', () => {
154+
it('Then, the left panel is expanded and all views ("Explorer", "Validation", "Views") are highlighted and active', () => {
154155
workbench.checkPanelState('left', 'expanded');
155156
workbench.isIconHighlighted('left', 'Explorer');
156157
workbench.isIconHighlighted('left', 'Validation');
157-
workbench.checkPanelContent('left', ['Explorer', 'Validation']);
158+
workbench.isIconHighlighted('left', 'Views');
159+
workbench.checkPanelContent('left', ['Explorer', 'Validation', "Views"]);
158160
});
159161

160162
it('Then, the right panel is expanded and all views ("Details", "Query", "Related Views", "Related Elements") are highlighted and active', () => {

integration-tests-cypress/cypress/e2e/project/workbench/workbench-configuration.data.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const workbenchConfigurationWithExpandedPanels: WorkbenchConfiguration =
4646
views: [
4747
{ id: 'explorer', isActive: true },
4848
{ id: 'validation', isActive: true },
49+
{ id: 'views-explorer', isActive: true },
4950
],
5051
},
5152
{

packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/studio/services/StudioWorkbenchConfigurationProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public WorkbenchConfiguration getWorkbenchConfiguration(String editingContextId)
6868
List.of(
6969
new WorkbenchSidePanelConfiguration("left", true, List.of(
7070
new DefaultViewConfiguration("explorer", true),
71+
new DefaultViewConfiguration("views-explorer", false),
7172
new DefaultViewConfiguration("validation", false),
7273
new DefaultViewConfiguration("search", false)
7374
)),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.sirius.web.application.views.viewsexplorer;
14+
15+
import java.util.UUID;
16+
17+
import org.eclipse.sirius.components.core.api.IInput;
18+
19+
/**
20+
* The input of the views explorer event subscription.
21+
*
22+
* @author tgiraudet
23+
*/
24+
public record ViewsExplorerEventInput(UUID id, String editingContextId, String representationId) implements IInput {
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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.sirius.web.application.views.viewsexplorer;
14+
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Objects;
18+
import java.util.Optional;
19+
20+
import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor;
21+
import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory;
22+
import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry;
23+
import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory;
24+
import org.eclipse.sirius.components.collaborative.trees.TreeEventProcessor;
25+
import org.eclipse.sirius.components.collaborative.trees.api.ITreeEventHandler;
26+
import org.eclipse.sirius.components.collaborative.trees.api.ITreeService;
27+
import org.eclipse.sirius.components.collaborative.trees.api.TreeCreationParameters;
28+
import org.eclipse.sirius.components.core.api.IEditingContext;
29+
import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService;
30+
import org.eclipse.sirius.components.core.api.IURLParser;
31+
import org.eclipse.sirius.components.trees.description.TreeDescription;
32+
import org.eclipse.sirius.web.application.views.viewsexplorer.services.ViewsExplorerTreeDescriptionProvider;
33+
import org.springframework.stereotype.Service;
34+
35+
import io.micrometer.core.instrument.MeterRegistry;
36+
37+
/**
38+
* Used to create the views explorer event processors.
39+
*
40+
* @author tgiraudet
41+
*/
42+
@Service
43+
public class ViewsExplorerEventProcessorFactory implements IRepresentationEventProcessorFactory {
44+
45+
private final IRepresentationDescriptionSearchService representationDescriptionSearchService;
46+
47+
private final ITreeService treeService;
48+
49+
private final List<ITreeEventHandler> treeEventHandlers;
50+
51+
private final ISubscriptionManagerFactory subscriptionManagerFactory;
52+
53+
private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry;
54+
55+
private final IURLParser urlParser;
56+
57+
private final MeterRegistry meterRegistry;
58+
59+
public ViewsExplorerEventProcessorFactory(IRepresentationDescriptionSearchService representationDescriptionSearchService, ITreeService treeService, List<ITreeEventHandler> treeEventHandlers,
60+
ISubscriptionManagerFactory subscriptionManagerFactory, IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry, IURLParser urlParser, MeterRegistry meterRegistry) {
61+
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
62+
this.treeService = Objects.requireNonNull(treeService);
63+
this.treeEventHandlers = Objects.requireNonNull(treeEventHandlers);
64+
this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory);
65+
this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry);
66+
this.urlParser = Objects.requireNonNull(urlParser);
67+
this.meterRegistry = Objects.requireNonNull(meterRegistry);
68+
}
69+
70+
@Override
71+
public boolean canHandle(IEditingContext editingContext, String representationId) {
72+
return representationId.startsWith(ViewsExplorerTreeDescriptionProvider.PREFIX);
73+
}
74+
75+
@Override
76+
public Optional<IRepresentationEventProcessor> createRepresentationEventProcessor(IEditingContext editingContext, String representationId) {
77+
Map<String, List<String>> parameters = this.urlParser.getParameterValues(representationId);
78+
Optional<TreeDescription> optionalTreeDescription = this.representationDescriptionSearchService
79+
.findById(editingContext, ViewsExplorerTreeDescriptionProvider.DESCRIPTION_ID)
80+
.filter(TreeDescription.class::isInstance)
81+
.map(TreeDescription.class::cast);
82+
83+
var expandedIdsParameter = parameters.get("expandedIds");
84+
85+
if (optionalTreeDescription.isPresent() && !expandedIdsParameter.isEmpty()) {
86+
var treeDescription = optionalTreeDescription.get();
87+
88+
String expandedIdsParam = parameters.get("expandedIds").get(0);
89+
var expanded = this.urlParser.getParameterEntries(expandedIdsParam);
90+
91+
TreeCreationParameters treeCreationParameters = TreeCreationParameters.newTreeCreationParameters(representationId)
92+
.treeDescription(treeDescription)
93+
.activeFilterIds(List.of())
94+
.expanded(expanded)
95+
.targetObject(editingContext)
96+
.editingContext(editingContext)
97+
.build();
98+
99+
IRepresentationEventProcessor treeEventProcessor = new TreeEventProcessor(editingContext, this.treeService, treeCreationParameters, this.treeEventHandlers,
100+
this.subscriptionManagerFactory.create(), this.meterRegistry, this.representationRefreshPolicyRegistry);
101+
return Optional.of(treeEventProcessor);
102+
}
103+
return Optional.empty();
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.sirius.web.application.views.viewsexplorer.controllers;
14+
15+
import com.fasterxml.jackson.databind.ObjectMapper;
16+
17+
import java.util.HashMap;
18+
import java.util.Map;
19+
import java.util.Objects;
20+
21+
import org.eclipse.sirius.components.annotations.spring.graphql.SubscriptionDataFetcher;
22+
import org.eclipse.sirius.components.core.api.IPayload;
23+
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
24+
import org.eclipse.sirius.components.graphql.api.IEventProcessorSubscriptionProvider;
25+
import org.eclipse.sirius.components.graphql.api.IExceptionWrapper;
26+
import org.eclipse.sirius.components.graphql.api.LocalContextConstants;
27+
import org.eclipse.sirius.web.application.views.viewsexplorer.ViewsExplorerEventInput;
28+
import org.reactivestreams.Publisher;
29+
30+
import graphql.execution.DataFetcherResult;
31+
import graphql.schema.DataFetchingEnvironment;
32+
33+
/**
34+
* The data fetcher used to send the Views Explorer view subscription.
35+
*
36+
* @author tgiraudet
37+
*/
38+
@SubscriptionDataFetcher(type = "Subscription", field = "viewsExplorerEvent")
39+
public class SubscriptionViewsExplorerEventDataFetcher implements IDataFetcherWithFieldCoordinates<Publisher<DataFetcherResult<IPayload>>> {
40+
41+
private static final String INPUT_ARGUMENT = "input";
42+
43+
private final ObjectMapper objectMapper;
44+
45+
private final IExceptionWrapper exceptionWrapper;
46+
47+
private final IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider;
48+
49+
public SubscriptionViewsExplorerEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) {
50+
this.objectMapper = Objects.requireNonNull(objectMapper);
51+
this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper);
52+
this.eventProcessorSubscriptionProvider = Objects.requireNonNull(eventProcessorSubscriptionProvider);
53+
}
54+
55+
@Override
56+
public Publisher<DataFetcherResult<IPayload>> get(DataFetchingEnvironment environment) throws Exception {
57+
Object argument = environment.getArgument(INPUT_ARGUMENT);
58+
var input = this.objectMapper.convertValue(argument, ViewsExplorerEventInput.class);
59+
60+
Map<String, Object> localContext = new HashMap<>();
61+
localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, input.editingContextId());
62+
localContext.put(LocalContextConstants.REPRESENTATION_ID, input.representationId());
63+
64+
return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), input.representationId(), input), input)
65+
.map(payload -> DataFetcherResult.<IPayload>newResult()
66+
.data(payload)
67+
.localContext(localContext)
68+
.build());
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.sirius.web.application.views.viewsexplorer.services;
14+
15+
import java.util.List;
16+
import java.util.Objects;
17+
18+
import org.eclipse.sirius.components.representations.IRepresentationDescription;
19+
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata;
20+
21+
/**
22+
* Object to represent a representation description type for the Views Explorer view.
23+
*
24+
* @author theogiraudet
25+
*/
26+
public record RepresentationDescriptionType(String id, IRepresentationDescription descriptions, List<RepresentationMetadata> representationsMetadata) {
27+
28+
public RepresentationDescriptionType {
29+
Objects.requireNonNull(id);
30+
Objects.requireNonNull(descriptions);
31+
Objects.requireNonNull(representationsMetadata);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.sirius.web.application.views.viewsexplorer.services;
14+
15+
import java.util.List;
16+
import java.util.Objects;
17+
18+
/**
19+
* Object to representation a representation kind for the Views Explorer view.
20+
*
21+
* @author theogiraudet
22+
*/
23+
public record RepresentationKind(String id, String name, List<RepresentationDescriptionType> representationDescriptionTypes) {
24+
25+
public RepresentationKind {
26+
Objects.requireNonNull(id);
27+
Objects.requireNonNull(name);
28+
Objects.requireNonNull(representationDescriptionTypes);
29+
}
30+
}

0 commit comments

Comments
 (0)