Skip to content

Commit 1aedfe1

Browse files
committed
[5050] Keep the dependencies of a project during the download / upload
Bug: #5050 Signed-off-by: Stéphane Bégaudeau <[email protected]>
1 parent ac8e96c commit 1aedfe1

File tree

7 files changed

+458
-3
lines changed

7 files changed

+458
-3
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ This will allow to integrate the command palette in other contexts than the work
166166
- https://github.com/eclipse-sirius/sirius-web/issues/5603[#5603] [sirius-web] Add a tree visualization of the impacts in the impact analysis dialog.
167167
- https://github.com/eclipse-sirius/sirius-web/issues/5766[#5766] [diagram] Merge all frontend palette into one common component
168168
- https://github.com/eclipse-sirius/sirius-web/issues/5563[#5563] [diagram] Contribute rectangular, image and edge appearance sections with the corresponding contribution point
169+
- https://github.com/eclipse-sirius/sirius-web/issues/5050[#5050] [sirius-web] Keep the dependencies of a project during the download / upload.
169170

170171

171172
== 2025.10.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 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.project.services;
14+
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Objects;
18+
import java.util.zip.ZipOutputStream;
19+
20+
import org.eclipse.sirius.web.application.UUIDParser;
21+
import org.eclipse.sirius.web.application.project.services.api.IProjectExportParticipant;
22+
import org.eclipse.sirius.web.domain.boundedcontexts.project.Project;
23+
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
24+
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticDataDependency;
25+
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService;
26+
import org.springframework.data.jdbc.core.mapping.AggregateReference;
27+
import org.springframework.stereotype.Service;
28+
29+
/**
30+
* Used to export the dependency data of a project.
31+
*
32+
* @author sbegaudeau
33+
*/
34+
@Service
35+
public class ProjectDependencyDataExportParticipant implements IProjectExportParticipant {
36+
37+
private final ISemanticDataSearchService semanticDataSearchService;
38+
39+
public ProjectDependencyDataExportParticipant(ISemanticDataSearchService semanticDataSearchService) {
40+
this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService);
41+
}
42+
43+
@Override
44+
public Map<String, Object> exportData(Project project, String editingContextId, ZipOutputStream outputStream) {
45+
var semanticDataDependencies = new UUIDParser().parse(editingContextId)
46+
.flatMap(this.semanticDataSearchService::findById)
47+
.map(SemanticData::getDependencies)
48+
.orElse(List.of());
49+
50+
var dependencies = semanticDataDependencies.stream()
51+
.map(SemanticDataDependency::dependencySemanticDataId)
52+
.map(AggregateReference::getId)
53+
.map(Objects::toString)
54+
.toList();
55+
56+
return Map.of("dependencies", dependencies);
57+
}
58+
}

packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/services/SemanticDataProjectContentImportParticipant.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.List;
1919
import java.util.Map;
2020
import java.util.Objects;
21+
import java.util.Optional;
2122
import java.util.UUID;
2223
import java.util.stream.Collectors;
2324

@@ -35,13 +36,16 @@
3536
import org.eclipse.sirius.web.application.project.services.api.IRewriteProxiesService;
3637
import org.eclipse.sirius.web.domain.boundedcontexts.project.events.ProjectCreatedEvent;
3738
import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.events.ProjectSemanticDataCreatedEvent;
39+
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
3840
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.events.SemanticDataCreatedEvent;
41+
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataUpdateService;
3942
import org.eclipse.sirius.web.domain.events.IDomainEvent;
4043
import org.eclipse.sirius.web.domain.services.Failure;
4144
import org.eclipse.sirius.web.domain.services.IResult;
4245
import org.eclipse.sirius.web.domain.services.Success;
4346
import org.slf4j.Logger;
4447
import org.slf4j.LoggerFactory;
48+
import org.springframework.data.jdbc.core.mapping.AggregateReference;
4549
import org.springframework.stereotype.Service;
4650

4751
/**
@@ -58,6 +62,8 @@ public class SemanticDataProjectContentImportParticipant implements IProjectCont
5862

5963
private final IEditingContextSearchService editingContextSearchService;
6064

65+
private final ISemanticDataUpdateService semanticDataUpdateService;
66+
6167
private final IUploadFileLoader uploadDocumentLoader;
6268

6369
private final IEditingContextPersistenceService editingContextPersistenceService;
@@ -67,10 +73,11 @@ public class SemanticDataProjectContentImportParticipant implements IProjectCont
6773
private final Logger logger = LoggerFactory.getLogger(SemanticDataProjectContentImportParticipant.class);
6874

6975
public SemanticDataProjectContentImportParticipant(
70-
IEditingContextSearchService editingContextSearchService, IUploadFileLoader uploadDocumentLoader, List<IUploadDocumentReportProvider> uploadDocumentReportProviders,
76+
IEditingContextSearchService editingContextSearchService, IUploadFileLoader uploadDocumentLoader, List<IUploadDocumentReportProvider> uploadDocumentReportProviders, ISemanticDataUpdateService semanticDataUpdateService,
7177
IEditingContextPersistenceService editingContextPersistenceService, IRewriteProxiesService rewriteProxiesService) {
7278
this.editingContextSearchService = Objects.requireNonNull(editingContextSearchService);
7379
this.uploadDocumentLoader = Objects.requireNonNull(uploadDocumentLoader);
80+
this.semanticDataUpdateService = Objects.requireNonNull(semanticDataUpdateService);
7481
this.editingContextPersistenceService = Objects.requireNonNull(editingContextPersistenceService);
7582
this.rewriteProxiesService = Objects.requireNonNull(rewriteProxiesService);
7683
}
@@ -89,6 +96,19 @@ public void handle(IDomainEvent event, ProjectZipContent projectContent) {
8996
&& projectSemanticDataCreatedEvent.causedBy() instanceof SemanticDataCreatedEvent semanticDataCreatedEvent
9097
&& semanticDataCreatedEvent.causedBy() instanceof ProjectCreatedEvent projectCreatedEvent
9198
&& projectCreatedEvent.causedBy() instanceof InitializeProjectInput initializeProjectInput) {
99+
var dependenciesEntry = initializeProjectInput.projectContent().manifest().get("dependencies");
100+
if (dependenciesEntry instanceof List<?> list) {
101+
var dependencies = list.stream()
102+
.filter(String.class::isInstance)
103+
.map(String.class::cast)
104+
.map(new UUIDParser()::parse)
105+
.flatMap(Optional::stream)
106+
.map(AggregateReference::<SemanticData, UUID> to)
107+
.toList();
108+
109+
this.semanticDataUpdateService.addDependencies(event, AggregateReference.to(semanticDataCreatedEvent.semanticData().getId()), dependencies);
110+
}
111+
92112
var documentsToUpload = this.getDocuments(projectContent);
93113
if (!documentsToUpload.isEmpty()) {
94114
var editingContextId = semanticDataCreatedEvent.semanticData().getId().toString();

packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/projects/ProjectDownloadControllerIntegrationTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public void givenStudioWhenTheDownloadOfProjectIsRequestedThenTheManifestIsExpor
104104
"https://www.eclipse.org/sirius/widgets/reference",
105105
"https://www.eclipse.org/sirius/widgets/tablewidget"
106106
],
107-
"representations":{}
107+
"representations":{},
108+
"dependencies": []
108109
}
109110
""";
110111

@@ -229,7 +230,8 @@ public void givenProjectWithRepresentationsWhenTheDownloadOfProjectIsRequestedTh
229230
"type": "siriusComponents://representation?type=Portal",
230231
"targetObjectURI": "sirius:///48dc942a-6b76-4133-bca5-5b29ebee133d#3237b215-ae23-48d7-861e-f542a4b9a4b8"
231232
}
232-
}
233+
},
234+
"dependencies": []
233235
}
234236
""";
235237

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024, 2025 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.controllers.projects;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.assertj.core.api.Assertions.fail;
17+
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.zip.ZipInputStream;
24+
25+
import org.eclipse.sirius.web.AbstractIntegrationTests;
26+
import org.eclipse.sirius.web.data.PapayaIdentifiers;
27+
import org.eclipse.sirius.web.tests.data.GivenSiriusWebServer;
28+
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.DisplayName;
31+
import org.junit.jupiter.api.Test;
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.boot.test.context.SpringBootTest;
34+
import org.springframework.boot.test.web.client.TestRestTemplate;
35+
import org.springframework.boot.test.web.server.LocalServerPort;
36+
import org.springframework.core.io.Resource;
37+
import org.springframework.http.HttpEntity;
38+
import org.springframework.http.HttpHeaders;
39+
import org.springframework.http.HttpMethod;
40+
import org.springframework.http.HttpStatus;
41+
import org.springframework.http.MediaType;
42+
import org.springframework.http.ResponseEntity;
43+
import org.springframework.transaction.annotation.Transactional;
44+
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
45+
46+
/**
47+
* Integration tests of the project download controllers for a project with dependencies.
48+
*
49+
* @author sbegaudeau
50+
*/
51+
@Transactional
52+
@SuppressWarnings("checkstyle:MultipleStringLiterals")
53+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
54+
public class ProjectWithDependenciesDownloadControllerIntegrationTests extends AbstractIntegrationTests {
55+
56+
@LocalServerPort
57+
private int port;
58+
59+
@Autowired
60+
private IGivenInitialServerState givenInitialServerState;
61+
62+
@BeforeEach
63+
public void beforeEach() {
64+
this.givenInitialServerState.initialize();
65+
}
66+
67+
@Test
68+
@GivenSiriusWebServer
69+
@DisplayName("Given a project with dependencies, when the download of the project is requested, then the manifest is exported")
70+
public void givenProjectWithDependenciesWhenTheDownloadOfProjectIsRequestedThenTheManifestIsExported() {
71+
var response = this.download(PapayaIdentifiers.PAPAYA_SAMPLE_PROJECT, null);
72+
73+
try (ZipInputStream inputStream = new ZipInputStream(response.getBody().getInputStream())) {
74+
HashMap<String, ByteArrayOutputStream> zipEntries = this.toZipEntries(inputStream);
75+
assertThat(zipEntries).isNotEmpty().containsKey("Papaya Sample/manifest.json");
76+
77+
String manifestContentExpected = """
78+
{
79+
"natures":[
80+
"siriusComponents://nature?kind=papaya"
81+
],
82+
"documentIdsToName":{
83+
"a4495c9c-d00c-4f0e-a591-1176d102a4a1":"Sirius Web Architecture",
84+
"56a66774-cbff-499f-a9fa-6b6600c86064":"GraphQL",
85+
"a0473b31-912f-4d99-8b41-3d44a8a1b238":"Sirius Web Project",
86+
"8139fdb7-bb71-4bca-b50b-9170870bbc0d":"Sirius Web Architecture"
87+
},
88+
"metamodels":[
89+
"domain://buck",
90+
"http://www.eclipse.org/emf/2002/Ecore",
91+
"https://www.eclipse.org/sirius-web/papaya"
92+
],
93+
"representations":{
94+
"dd0080f8-430d-441f-99a4-f46c7d9b28ef":{
95+
"targetObjectURI":"sirius:///a4495c9c-d00c-4f0e-a591-1176d102a4a1#569d3f9b-2a43-4254-b609-511258251d96",
96+
"type":"siriusComponents://representation?type=Table",
97+
"descriptionURI":"papaya_package_table_description"
98+
}
99+
},
100+
"dependencies":[
101+
"6f24a044-1605-484d-96c3-553ff6bc184d"
102+
]
103+
}
104+
""";
105+
106+
String manifestContent = zipEntries.get("Papaya Sample/manifest.json").toString(StandardCharsets.UTF_8);
107+
var objectMapper = new ObjectMapper();
108+
assertThat(objectMapper.readTree(manifestContent)).isEqualTo(objectMapper.readTree(manifestContentExpected));
109+
} catch (IOException exception) {
110+
fail(exception.getMessage());
111+
}
112+
}
113+
114+
private ResponseEntity<Resource> download(String projectId, String name) {
115+
var uri = "http://localhost:" + this.port + "/api/projects/" + projectId;
116+
if (name != null) {
117+
uri += "?name=" + name;
118+
}
119+
120+
HttpHeaders headers = new HttpHeaders();
121+
headers.setAccept(List.of(MediaType.parseMediaType("application/zip")));
122+
HttpEntity<String> entity = new HttpEntity<>(null, headers);
123+
124+
var response = new TestRestTemplate().exchange(uri, HttpMethod.GET, entity, Resource.class);
125+
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
126+
127+
return response;
128+
}
129+
130+
private HashMap<String, ByteArrayOutputStream> toZipEntries(ZipInputStream inputStream) {
131+
HashMap<String, ByteArrayOutputStream> zipEntries = new HashMap<>();
132+
133+
try {
134+
var zipEntry = inputStream.getNextEntry();
135+
while (zipEntry != null) {
136+
if (!zipEntry.isDirectory()) {
137+
String name = zipEntry.getName();
138+
ByteArrayOutputStream entryBaos = new ByteArrayOutputStream();
139+
inputStream.transferTo(entryBaos);
140+
zipEntries.put(name, entryBaos);
141+
}
142+
zipEntry = inputStream.getNextEntry();
143+
}
144+
} catch (IOException exception) {
145+
fail(exception.getMessage());
146+
}
147+
148+
return zipEntries;
149+
}
150+
151+
}

0 commit comments

Comments
 (0)