Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
SysON now uses its own indexing logic instead of the default one provided by Sirius Web.
This allows to limit the size of the indices, and ensure information stored in the indices are useful to perform cross-project search.
You can find more information on how to setup Elasticsearch, how elements are mapped to index documents, and how to query them in the documentation.
- https://github.com/eclipse-syson/syson/issues/1649[#1649] [syson] Add support for publishing representations with libraries.
Publishing a project that contains representations now produces libraries that also contain the representations.

=== New features

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*******************************************************************************
* Copyright (c) 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
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.syson.application.publication;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.eclipse.sirius.web.application.UUIDParser;
import org.eclipse.sirius.web.application.library.dto.PublishLibrariesInput;
import org.eclipse.sirius.web.application.project.services.api.IProjectEditingContextService;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationContent;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.RepresentationCompositeIdProvider;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationContentCreationService;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationContentSearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataCreationService;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataSearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.events.SemanticDataCreatedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

/**
* Creates the representations of a library once its semantic data are published.
*
* @author gdaniel
*/
@Service
public class SysONLibraryRepresentationPublicationListener {

private final IRepresentationMetadataSearchService representationMetadataSearchService;

private final IRepresentationMetadataCreationService representationMetadataCreationService;

private final IRepresentationContentSearchService representationContentSearchService;

private final IRepresentationContentCreationService representationContentCreationService;

private final IProjectEditingContextService projectEditingContextService;

private final Logger logger = LoggerFactory.getLogger(SysONLibraryRepresentationPublicationListener.class);

public SysONLibraryRepresentationPublicationListener(IRepresentationMetadataSearchService representationMetadataSearchService,
IRepresentationMetadataCreationService representationMetadataCreationService, IRepresentationContentSearchService representationContentSearchService,
IRepresentationContentCreationService representationContentCreationService,
IProjectEditingContextService projectEditingContextService) {
this.representationMetadataSearchService = Objects.requireNonNull(representationMetadataSearchService);
this.representationMetadataCreationService = Objects.requireNonNull(representationMetadataCreationService);
this.representationContentSearchService = Objects.requireNonNull(representationContentSearchService);
this.representationContentCreationService = Objects.requireNonNull(representationContentCreationService);
this.projectEditingContextService = Objects.requireNonNull(projectEditingContextService);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener
public void onSemanticDataCreatedEvent(final SemanticDataCreatedEvent semanticDataCreatedEvent) {
if (semanticDataCreatedEvent.causedBy() instanceof SysONPublishedLibrarySemanticDataCreationRequested request
&& request.causedBy() instanceof PublishLibrariesInput publishLibrariesInput) {
// TODO once we merge the PR in SW we should have direct access to the editing context, not the project id
Optional<AggregateReference<SemanticData, UUID>> optionalSemanticDataId = this.projectEditingContextService.getEditingContextId(publishLibrariesInput.projectId())
.flatMap(id -> new UUIDParser().parse(id))
.map(AggregateReference::to);

if (optionalSemanticDataId.isPresent()) {

SemanticData librarySemanticData = semanticDataCreatedEvent.semanticData();
List<RepresentationMetadata> representationMetadataOnSemanticData = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(optionalSemanticDataId.get());

for (RepresentationMetadata representationMetadata : representationMetadataOnSemanticData) {

Optional<RepresentationContent> optionalRepresentationContent = this.representationContentSearchService.findContentById(optionalSemanticDataId.get(),
AggregateReference.to(representationMetadata.getRepresentationMetadataId()));

if (optionalRepresentationContent.isPresent()) {

var duplicatedRepresentationId = UUID.randomUUID();
var id = new RepresentationCompositeIdProvider().getId(librarySemanticData.getId(), duplicatedRepresentationId);
var duplicatedRepresentationMetadata = RepresentationMetadata.newRepresentationMetadata(id)
.representationMetadataId(duplicatedRepresentationId)
.semanticData(AggregateReference.to(librarySemanticData.getId()))
.targetObjectId(representationMetadata.getTargetObjectId()) // same target object id (ids don't change in libraries)
.descriptionId(representationMetadata.getDescriptionId())
.label(representationMetadata.getLabel())
.kind(representationMetadata.getKind())
.documentation(representationMetadata.getDocumentation())
.iconURLs(representationMetadata.getIconURLs())
.build(semanticDataCreatedEvent);
this.representationMetadataCreationService.create(duplicatedRepresentationMetadata);

var duplicatedContent = optionalRepresentationContent.get().getContent()
.replace(representationMetadata.getRepresentationMetadataId().toString(), duplicatedRepresentationId.toString());
this.representationContentCreationService.create(
semanticDataCreatedEvent,
duplicatedRepresentationMetadata.getSemanticData(),
AggregateReference.to(duplicatedRepresentationMetadata.getRepresentationMetadataId()),
duplicatedContent,
optionalRepresentationContent.get().getLastMigrationPerformed(),
optionalRepresentationContent.get().getMigrationVersion()
);
} else {
this.logger.warn("Cannot find representation content with id {}", representationMetadata.getRepresentationMetadataId());
}
}
} else {
// TODO editingContextId once we get the SW update
this.logger.warn("Cannot find semantic datta witht id {}", publishLibrariesInput.projectId());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import org.eclipse.sirius.web.application.library.dto.PublishLibrariesInput;
import org.eclipse.sirius.web.domain.boundedcontexts.library.Library;
import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibrarySearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataSearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticDataDependency;
Expand Down Expand Up @@ -95,6 +97,9 @@ public class SysONLibraryPublicationTests extends AbstractIntegrationTests {
@Autowired
private ILibrarySearchService librarySearchService;

@Autowired
private IRepresentationMetadataSearchService representationMetadataSearchService;

@Autowired
private PublishLibrariesMutationRunner publishLibrariesMutationRunner;

Expand Down Expand Up @@ -176,12 +181,16 @@ public void givenProjectWhenLibraryIsPublishedThenContentOfLibraryMatchesContent
TestTransaction.end();
TestTransaction.start();

final Optional<IEditingContext> maybeLibraryEditingContext = this.librarySearchService
final Optional<AggregateReference<SemanticData, UUID>> maybeSemanticDataId = this.librarySearchService
.findByNamespaceAndNameAndVersion(SimpleProjectElementsTestProjectData.PROJECT_ID, SimpleProjectElementsTestProjectData.PROJECT_NAME, LIBRARY_VERSION)
.map(Library::getSemanticData)
.map(AggregateReference::getId)
.map(UUID::toString)
.flatMap(this.editingContextSearchService::findById);
.map(Library::getSemanticData);
assertThat(maybeSemanticDataId).isPresent();

// Check that there is no representation associated to the library's semantic data
List<RepresentationMetadata> libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(maybeSemanticDataId.get());
assertThat(libraryRepresentationMetadata).isEmpty();

final Optional<IEditingContext> maybeLibraryEditingContext = this.editingContextSearchService.findById(maybeSemanticDataId.get().getId().toString());
assertThat(maybeLibraryEditingContext).isPresent().get().isInstanceOf(IEMFEditingContext.class);
final ResourceSet libraryResourceSet = ((IEMFEditingContext) maybeLibraryEditingContext.get()).getDomain().getResourceSet();

Expand Down Expand Up @@ -251,6 +260,11 @@ public void givenProjectWithImportedResourceWhenLibraryIsPublishedThenLibraryDoe
.flatMap(this.semanticDataSearchService::findById);

assertThat(semanticData).isPresent();

// Check that there is no representation associated to the library's semantic data
List<RepresentationMetadata> libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(AggregateReference.to(semanticData.get().getId()));
assertThat(libraryRepresentationMetadata).isEmpty();

Set<Document> documents = semanticData.get().getDocuments();
assertThat(documents)
.hasSize(1)
Expand Down Expand Up @@ -285,6 +299,12 @@ public void givenProjectWithUsedDependencyToLibraryWhenLibraryIsPublishedThenItH
assertThat(dependencyLibrary).isPresent();
assertThat(dependencyLibrary.get().getName()).isEqualTo("Batmobile");

// Check that a representation is associated to the library's semantic data
List<RepresentationMetadata> libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(AggregateReference.to(projectLibrarySemanticData.get().getId()));
assertThat(libraryRepresentationMetadata).hasSize(1)
.anySatisfy(representationMetadata -> assertThat(representationMetadata.getLabel()).isEqualTo("General View"));


// Check that the library contains a single document (its proper content).
Set<Document> documents = projectLibrarySemanticData.get().getDocuments();
assertThat(documents)
Expand Down Expand Up @@ -316,6 +336,10 @@ public void givenProjectWithUnusedDependencyToLibraryWhenLibraryIsPublishedThenI
assertThat(projectLibrarySemanticData).isPresent();
// Check that there is no dependency in the published library.
assertThat(projectLibrarySemanticData.get().getDependencies()).isEmpty();
// Check that a representation is associated to the library's semantic data
List<RepresentationMetadata> libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(AggregateReference.to(projectLibrarySemanticData.get().getId()));
assertThat(libraryRepresentationMetadata).hasSize(1)
.anySatisfy(representationMetadata -> assertThat(representationMetadata.getLabel()).isEqualTo("General View"));

// Check that the library contains a single document (its proper content).
Set<Document> documents = projectLibrarySemanticData.get().getDocuments();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ It's not recommended for production use.

== Improvements

* Add support for publishing representations with libraries.
Publishing a project that contains representations now produces libraries that also contain the representations.
Users can navigate to `/libraries/<library identifier>` to open a library and see its representations.

== Technical details

* For technical details on this {product} release (including breaking changes), please refer to https://github.com/eclipse-syson/syson/blob/main/CHANGELOG.adoc[changelog].
Loading