From 1dda67f4f72b83757a620f073dbf4fc0f4d844f0 Mon Sep 17 00:00:00 2001 From: Gwendal Daniel Date: Thu, 22 Jan 2026 16:26:49 +0100 Subject: [PATCH] [1649] Add support for publishing representations with libraries Bug: https://github.com/eclipse-syson/syson/issues/1649 Signed-off-by: Gwendal Daniel --- CHANGELOG.adoc | 2 + ...raryRepresentationPublicationListener.java | 127 ++++++++++++++++++ .../SysONLibraryPublicationTests.java | 34 ++++- .../pages/release-notes/2026.3.0.adoc | 4 + 4 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONLibraryRepresentationPublicationListener.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 285883e23..6c03f1fbb 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -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 diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONLibraryRepresentationPublicationListener.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONLibraryRepresentationPublicationListener.java new file mode 100644 index 000000000..cac01689d --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONLibraryRepresentationPublicationListener.java @@ -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> optionalSemanticDataId = this.projectEditingContextService.getEditingContextId(publishLibrariesInput.projectId()) + .flatMap(id -> new UUIDParser().parse(id)) + .map(AggregateReference::to); + + if (optionalSemanticDataId.isPresent()) { + + SemanticData librarySemanticData = semanticDataCreatedEvent.semanticData(); + List representationMetadataOnSemanticData = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(optionalSemanticDataId.get()); + + for (RepresentationMetadata representationMetadata : representationMetadataOnSemanticData) { + + Optional 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()); + } + } + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/publication/SysONLibraryPublicationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/publication/SysONLibraryPublicationTests.java index 7de3dda2d..21ec32cd7 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/publication/SysONLibraryPublicationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/libraries/publication/SysONLibraryPublicationTests.java @@ -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; @@ -95,6 +97,9 @@ public class SysONLibraryPublicationTests extends AbstractIntegrationTests { @Autowired private ILibrarySearchService librarySearchService; + @Autowired + private IRepresentationMetadataSearchService representationMetadataSearchService; + @Autowired private PublishLibrariesMutationRunner publishLibrariesMutationRunner; @@ -176,12 +181,16 @@ public void givenProjectWhenLibraryIsPublishedThenContentOfLibraryMatchesContent TestTransaction.end(); TestTransaction.start(); - final Optional maybeLibraryEditingContext = this.librarySearchService + final Optional> 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 libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(maybeSemanticDataId.get()); + assertThat(libraryRepresentationMetadata).isEmpty(); + + final Optional maybeLibraryEditingContext = this.editingContextSearchService.findById(maybeSemanticDataId.get().getId().toString()); assertThat(maybeLibraryEditingContext).isPresent().get().isInstanceOf(IEMFEditingContext.class); final ResourceSet libraryResourceSet = ((IEMFEditingContext) maybeLibraryEditingContext.get()).getDomain().getResourceSet(); @@ -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 libraryRepresentationMetadata = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticData(AggregateReference.to(semanticData.get().getId())); + assertThat(libraryRepresentationMetadata).isEmpty(); + Set documents = semanticData.get().getDocuments(); assertThat(documents) .hasSize(1) @@ -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 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 documents = projectLibrarySemanticData.get().getDocuments(); assertThat(documents) @@ -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 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 documents = projectLibrarySemanticData.get().getDocuments(); diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc index 160ddfc75..454e84c02 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc @@ -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/` 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]. \ No newline at end of file