From bb225ad9c65c2cd403a7f5fd340cc592cd4b5831 Mon Sep 17 00:00:00 2001 From: Michael Charfadi Date: Mon, 20 Jan 2025 13:29:00 +0100 Subject: [PATCH] [4444] Allow multiple implementation of IEditingContextSaver & IEditingContextLoader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/eclipse-sirius/sirius-web/issues/4444 Signed-off-by: Michaƫl Charfadi --- CHANGELOG.adoc | 2 + .../ViewerEditingContextDataFetcher.java | 5 +- .../services/EditingContextLoader.java | 33 ++++- .../EditingContextPersistenceService.java | 59 ++------ .../services/EditingContextSaver.java | 120 ++++++++++++++++ .../services/EditingContextSearchService.java | 60 ++++---- .../services/api/IEditingContextLoader.java | 5 +- .../services/api/IEditingContextSaver.java | 28 ++++ .../controllers/ViewerProjectDataFetcher.java | 6 + .../semanticdata/SemanticData.java | 9 +- .../repositories/ISemanticDataRepository.java | 3 +- .../services/SemanticDataCreationService.java | 11 ++ .../services/SemanticDataSearchService.java | 8 +- .../services/SemanticDataUpdateService.java | 7 +- .../api/ISemanticDataCreationService.java | 4 + .../api/ISemanticDataSearchService.java | 3 + .../api/ISemanticDataUpdateService.java | 5 +- .../PapayaEditingContextInitializer.java | 2 + .../services/PapayaEditingContextLoader.java | 131 ++++++++++++++++++ .../services/PapayaEditingContextSaver.java | 106 ++++++++++++++ .../sirius/web/domain/DomainEventsTest.java | 6 +- 21 files changed, 510 insertions(+), 103 deletions(-) create mode 100644 packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSaver.java create mode 100644 packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextSaver.java create mode 100644 packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextLoader.java create mode 100644 packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextSaver.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7905898010..9da7665b8e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -29,6 +29,7 @@ It will allow us to create more complex test data shared by all integration tests to detect additional problems. - https://github.com/eclipse-sirius/sirius-web/issues/4416[#4416] [sirius-web] The `IEditingContextMigrationParticipantPredicate` will now use the id of the editing context instead of the whole editing context in order to find out if the migration participant should be activated. - https://github.com/eclipse-sirius/sirius-web/issues/4418[#4418] [sirius-web] Change type of project_id from UUID to String +- https://github.com/eclipse-sirius/sirius-web/issues/4444[#4444] [sirius-web] Allow multiple implementation of IEditingContextSaver & IEditingContextLoader === Dependency update @@ -77,6 +78,7 @@ It can be activated using `sirius.web.graphql.tracing=true` since it is not enab Some additional log has also been contributed on the frontend in order to view more easily the order and time of the GraphQL requests and responses. - https://github.com/eclipse-sirius/sirius-web/issues/4430[#4430] [diagram] Add the PNG export of a diagram. - https://github.com/eclipse-sirius/sirius-web/issues/4346[#4346] [query] The current selection is now available using both the variables `selection: Sequence{Object}` for the full selection and `self: Object` for the first selected object. +- https://github.com/eclipse-sirius/sirius-web/issues/4444[#4444] [sirius-web] Allow multiple implementation of IEditingContextSaver & IEditingContextLoader === Improvements diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/controllers/ViewerEditingContextDataFetcher.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/controllers/ViewerEditingContextDataFetcher.java index 2e71300268..3a0b926912 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/controllers/ViewerEditingContextDataFetcher.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/controllers/ViewerEditingContextDataFetcher.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -43,9 +43,6 @@ public ViewerEditingContextDataFetcher(IEditingContextApplicationService editing @Override public DataFetcherResult get(DataFetchingEnvironment environment) throws Exception { String editingContextId = environment.getArgument(EDITING_CONTEXT_ID_ARGUMENT); - if (!this.editingContextApplicationService.existsById(editingContextId)) { - return null; - } Map localContext = new HashMap<>(); localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, editingContextId); diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextLoader.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextLoader.java index a3213b1533..cd7003abac 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextLoader.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextLoader.java @@ -12,18 +12,25 @@ *******************************************************************************/ package org.eclipse.sirius.web.application.editingcontext.services; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Objects; import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IEditingContextProcessor; import org.eclipse.sirius.components.core.api.IEditingContextRepresentationDescriptionProvider; import org.eclipse.sirius.components.emf.services.EditingContextCrossReferenceAdapter; import org.eclipse.sirius.emfjson.resource.JsonResource; +import org.eclipse.sirius.web.application.UUIDParser; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextLoader; import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingDomainFactory; import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceLoader; +import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService; import org.slf4j.Logger; @@ -49,17 +56,34 @@ public class EditingContextLoader implements IEditingContextLoader { private final List editingContextProcessors; + private final IProjectSearchService projectSearchService; + private final List migrationParticipantPredicates; - public EditingContextLoader(ISemanticDataSearchService semanticDataSearchService, IResourceLoader resourceLoader, List representationDescriptionProviders, List editingContextProcessors, List migrationParticipantPredicates) { + private final IEditingDomainFactory editingDomainFactory; + + public EditingContextLoader(ISemanticDataSearchService semanticDataSearchService, IResourceLoader resourceLoader, List representationDescriptionProviders, List editingContextProcessors, IProjectSearchService projectSearchService, List migrationParticipantPredicates, IEditingDomainFactory editingDomainFactory) { this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService); this.resourceLoader = Objects.requireNonNull(resourceLoader); this.representationDescriptionProviders = Objects.requireNonNull(representationDescriptionProviders); this.editingContextProcessors = Objects.requireNonNull(editingContextProcessors); + this.projectSearchService = Objects.requireNonNull(projectSearchService); this.migrationParticipantPredicates = Objects.requireNonNull(migrationParticipantPredicates); + this.editingDomainFactory = Objects.requireNonNull(editingDomainFactory); } - public void load(EditingContext editingContext, String projectId) { + public IEditingContext load(String editingContextId) { + var project = this.projectSearchService.findById(editingContextId); + + AdapterFactoryEditingDomain editingDomain = this.editingDomainFactory.createEditingDomain(project.get()); + EditingContext editingContext = new EditingContext(project.get().getId(), editingDomain, new HashMap<>(), new ArrayList<>()); + + this.toEditingContext(editingContext, project.get().getId()); + + return editingContext; + } + + public void toEditingContext(EditingContext editingContext, String projectId) { this.editingContextProcessors.forEach(processor -> processor.preProcess(editingContext)); this.semanticDataSearchService.findByProject(AggregateReference.to(projectId)) @@ -73,6 +97,11 @@ public void load(EditingContext editingContext, String projectId) { this.editingContextProcessors.forEach(processor -> processor.postProcess(editingContext)); } + @Override + public boolean canHandle(String projectId) { + return new UUIDParser().parse(projectId).isPresent() && this.projectSearchService.existsById(projectId); + } + private void loadSemanticData(EditingContext editingContext, SemanticData semanticData) { ResourceSet resourceSet = editingContext.getDomain().getResourceSet(); resourceSet.getLoadOptions().put(JsonResource.OPTION_SCHEMA_LOCATION, true); diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextPersistenceService.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextPersistenceService.java index cda360f7ee..7623e7a550 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextPersistenceService.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextPersistenceService.java @@ -12,32 +12,21 @@ *******************************************************************************/ package org.eclipse.sirius.web.application.editingcontext.services; -import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IEditingContextPersistenceService; -import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.components.events.ICause; -import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate; -import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextPersistenceFilter; -import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceToDocumentService; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; -import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document; -import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataUpdateService; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextSaver; 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.Transactional; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; - /** * Used to save the editing context. * @@ -48,23 +37,14 @@ public class EditingContextPersistenceService implements IEditingContextPersiste private static final String TIMER_NAME = "siriusweb_editingcontext_save"; - private final ISemanticDataUpdateService semanticDataUpdateService; - - private final IResourceToDocumentService resourceToDocumentService; + private final List editingContextSavers; - private final List persistenceFilters; - - private final List migrationParticipantPredicates; + private final Logger logger = LoggerFactory.getLogger(EditingContextPersistenceService.class); private final Timer timer; - private final Logger logger = LoggerFactory.getLogger(EditingContextPersistenceService.class); - - public EditingContextPersistenceService(ISemanticDataUpdateService semanticDataUpdateService, IResourceToDocumentService resourceToDocumentService, List persistenceFilters, List migrationParticipantPredicates, MeterRegistry meterRegistry) { - this.semanticDataUpdateService = Objects.requireNonNull(semanticDataUpdateService); - this.resourceToDocumentService = Objects.requireNonNull(resourceToDocumentService); - this.persistenceFilters = Objects.requireNonNull(persistenceFilters); - this.migrationParticipantPredicates = Objects.requireNonNull(migrationParticipantPredicates); + public EditingContextPersistenceService(List editingContextSavers, MeterRegistry meterRegistry) { + this.editingContextSavers = Objects.requireNonNull(editingContextSavers); this.timer = Timer.builder(TIMER_NAME).register(meterRegistry); } @@ -73,28 +53,9 @@ public EditingContextPersistenceService(ISemanticDataUpdateService semanticDataU public void persist(ICause cause, IEditingContext editingContext) { long start = System.currentTimeMillis(); - if (editingContext instanceof IEMFEditingContext emfEditingContext) { - var applyMigrationParticipants = this.migrationParticipantPredicates.stream().anyMatch(predicate -> predicate.test(emfEditingContext.getId())); - AggregateReference projectId = AggregateReference.to(editingContext.getId()); - - var documentData = emfEditingContext.getDomain().getResourceSet().getResources().stream() - .filter(resource -> IEMFEditingContext.RESOURCE_SCHEME.equals(resource.getURI().scheme())) - .filter(resource -> this.persistenceFilters.stream().allMatch(filter -> filter.shouldPersist(resource))) - .map(resource -> this.resourceToDocumentService.toDocument(resource, applyMigrationParticipants)) - .flatMap(Optional::stream) - .collect(Collectors.toSet()); - - var documents = new LinkedHashSet(); - var domainUris = new LinkedHashSet(); - - documentData.forEach(data -> { - documents.add(data.document()); - domainUris.addAll(data.ePackageEntries().stream().map(EPackageEntry::nsURI).toList()); - }); - - this.semanticDataUpdateService.updateDocuments(cause, projectId, documents, domainUris); - } - + editingContextSavers.stream() + .filter(editingContextSaver -> editingContextSaver.canHandle(editingContext)) + .forEach(editingContextSaver -> editingContextSaver.save(cause, editingContext)); long end = System.currentTimeMillis(); this.timer.record(end - start, TimeUnit.MILLISECONDS); diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSaver.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSaver.java new file mode 100644 index 0000000000..a216bb8936 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSaver.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2025 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.sirius.web.application.editingcontext.services; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IEditingContextSearchService; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.events.ICause; +import org.eclipse.sirius.web.application.UUIDParser; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextPersistenceFilter; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextSaver; +import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceToDocumentService; +import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataUpdateService; +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.Transactional; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Used to save an editing context. + * + * @author mcharfadi + */ +@Service +public class EditingContextSaver implements IEditingContextSaver { + + private static final String TIMER_NAME = "siriusweb_editingcontext_save"; + + private final ISemanticDataUpdateService semanticDataUpdateService; + + private final IResourceToDocumentService resourceToDocumentService; + + private final List persistenceFilters; + + private final List migrationParticipantPredicates; + + private final ISemanticDataSearchService semanticDataSearchService; + + private final Timer timer; + + private final Logger logger = LoggerFactory.getLogger(EditingContextSaver.class); + + public EditingContextSaver(ISemanticDataUpdateService semanticDataUpdateService, IResourceToDocumentService resourceToDocumentService, IEditingContextSearchService editingContextSearchService, List persistenceFilters, List migrationParticipantPredicates, ISemanticDataSearchService semanticDataSearchService, MeterRegistry meterRegistry) { + this.semanticDataUpdateService = Objects.requireNonNull(semanticDataUpdateService); + this.resourceToDocumentService = Objects.requireNonNull(resourceToDocumentService); + this.persistenceFilters = Objects.requireNonNull(persistenceFilters); + this.migrationParticipantPredicates = Objects.requireNonNull(migrationParticipantPredicates); + this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService); + this.timer = Timer.builder(TIMER_NAME).register(meterRegistry); + } + + @Transactional + @Override + public void save(ICause cause, IEditingContext editingContext) { + long start = System.currentTimeMillis(); + + if (editingContext instanceof IEMFEditingContext emfEditingContext) { + var applyMigrationParticipants = this.migrationParticipantPredicates.stream().anyMatch(predicate -> predicate.test(emfEditingContext.getId())); + AggregateReference projectId = AggregateReference.to(editingContext.getId()); + + var documentData = emfEditingContext.getDomain().getResourceSet().getResources().stream() + .filter(resource -> IEMFEditingContext.RESOURCE_SCHEME.equals(resource.getURI().scheme())) + .filter(resource -> this.persistenceFilters.stream().allMatch(filter -> filter.shouldPersist(resource))) + .map(resource -> this.resourceToDocumentService.toDocument(resource, applyMigrationParticipants)) + .flatMap(Optional::stream) + .collect(Collectors.toSet()); + + var documents = new LinkedHashSet(); + var domainUris = new LinkedHashSet(); + + documentData.forEach(data -> { + documents.add(data.document()); + domainUris.addAll(data.ePackageEntries().stream().map(EPackageEntry::nsURI).toList()); + }); + + this.semanticDataSearchService.findByProject(projectId).ifPresent(semanticData -> { + this.semanticDataUpdateService.updateDocuments(cause, semanticData.getId(), documents, domainUris); + }); + + } + + long end = System.currentTimeMillis(); + this.timer.record(end - start, TimeUnit.MILLISECONDS); + + this.logger.atDebug() + .setMessage("EditingContext {}: {}ms to persist the semantic data") + .addArgument(editingContext.getId()) + .addArgument(() -> String.format("%1$6s", end - start)) + .log(); + } + + @Override + public boolean canHandle(IEditingContext editingContext) { + return editingContext instanceof IEMFEditingContext && new UUIDParser().parse(editingContext.getId()).isPresent(); + } +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSearchService.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSearchService.java index 76ac0d2b09..fd2369e08a 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSearchService.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/EditingContextSearchService.java @@ -12,8 +12,7 @@ *******************************************************************************/ package org.eclipse.sirius.web.application.editingcontext.services; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Spliterator; @@ -21,13 +20,11 @@ import java.util.concurrent.TimeUnit; import java.util.stream.StreamSupport; -import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IEditingContextSearchService; +import org.eclipse.sirius.web.application.UUIDParser; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextLoader; -import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingDomainFactory; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,56 +46,55 @@ public class EditingContextSearchService implements IEditingContextSearchService private final Logger logger = LoggerFactory.getLogger(EditingContextSearchService.class); - private final IProjectSearchService projectSearchService; - - private final IEditingDomainFactory editingDomainFactory; + private final List editingContextLoader; - private final IEditingContextLoader editingContextLoader; + private final IProjectSearchService projectSearchService; private final Timer timer; - public EditingContextSearchService(IProjectSearchService projectSearchService, IEditingDomainFactory editingDomainFactory, IEditingContextLoader editingContextLoader, MeterRegistry meterRegistry) { - this.projectSearchService = Objects.requireNonNull(projectSearchService); - this.editingDomainFactory = Objects.requireNonNull(editingDomainFactory); + public EditingContextSearchService(List editingContextLoader, IProjectSearchService projectSearchService, MeterRegistry meterRegistry) { this.editingContextLoader = Objects.requireNonNull(editingContextLoader); + this.projectSearchService = Objects.requireNonNull(projectSearchService); this.timer = Timer.builder(TIMER_NAME).register(meterRegistry); } @Override @Transactional(readOnly = true) public boolean existsById(String editingContextId) { + var normalizedId = editingContextId.split("\\+"); + if (normalizedId.length == 2 && new UUIDParser().parse(normalizedId[0]).isPresent() && new UUIDParser().parse(normalizedId[1]).isPresent()) { + return this.projectSearchService.existsById(normalizedId[0]); + } return this.projectSearchService.existsById(editingContextId); } @Override @Transactional(readOnly = true) public Optional findById(String editingContextId) { - return this.projectSearchService.findById(editingContextId) - .map(this::toEditingContext); - } - - private IEditingContext toEditingContext(Project project) { long start = System.currentTimeMillis(); - AdapterFactoryEditingDomain editingDomain = this.editingDomainFactory.createEditingDomain(project); - EditingContext editingContext = new EditingContext(project.getId().toString(), editingDomain, new HashMap<>(), new ArrayList<>()); - this.editingContextLoader.load(editingContext, project.getId()); + var optionalEditingContext = this.editingContextLoader.stream() + .filter(loader -> loader.canHandle(editingContextId)) + .findFirst() + .map(loader -> loader.load(editingContextId)); long end = System.currentTimeMillis(); this.timer.record(end - start, TimeUnit.MILLISECONDS); - this.logger.atDebug() - .setMessage("EditingContext {}: {}ms to load {} objects") - .addArgument(project.getId()) - .addArgument(() -> String.format("%1$6s", end - start)) - .addArgument(() -> { - var iterator = editingDomain.getResourceSet().getAllContents(); - var stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false); - return stream.count(); - }) - .log(); - - return editingContext; + if (optionalEditingContext.isPresent() && optionalEditingContext.get() instanceof EditingContext editingContext) { + this.logger.atDebug() + .setMessage("EditingContext {}: {}ms to load {} objects") + .addArgument(editingContextId) + .addArgument(() -> String.format("%1$6s", end - start)) + .addArgument(() -> { + var iterator = editingContext.getDomain().getResourceSet().getAllContents(); + var stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false); + return stream.count(); + }) + .log(); + } + + return optionalEditingContext; } } diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextLoader.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextLoader.java index 36a759b798..c92a71eb02 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextLoader.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextLoader.java @@ -12,7 +12,7 @@ *******************************************************************************/ package org.eclipse.sirius.web.application.editingcontext.services.api; -import org.eclipse.sirius.web.application.editingcontext.EditingContext; +import org.eclipse.sirius.components.core.api.IEditingContext; /** * Used to load an editing context. @@ -21,6 +21,7 @@ */ public interface IEditingContextLoader { - void load(EditingContext editingContext, String projectId); + IEditingContext load(String editingContextId); + boolean canHandle(String projectId); } diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextSaver.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextSaver.java new file mode 100644 index 0000000000..d30b854848 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/editingcontext/services/api/IEditingContextSaver.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2025 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.sirius.web.application.editingcontext.services.api; + +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.events.ICause; + +/** + * Used to load an editing context. + * + * @author mcharfadi + */ +public interface IEditingContextSaver { + + void save(ICause cause, IEditingContext editingContext); + + boolean canHandle(IEditingContext editingContext); +} diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ViewerProjectDataFetcher.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ViewerProjectDataFetcher.java index 6a52687070..f0aedd82af 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ViewerProjectDataFetcher.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ViewerProjectDataFetcher.java @@ -16,6 +16,7 @@ import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.web.application.UUIDParser; import org.eclipse.sirius.web.application.project.services.api.IProjectApplicationService; import org.eclipse.sirius.web.application.project.dto.ProjectDTO; @@ -40,6 +41,11 @@ public ViewerProjectDataFetcher(IProjectApplicationService projectApplicationSer @Override public ProjectDTO get(DataFetchingEnvironment environment) throws Exception { String projectId = environment.getArgument(PROJECT_ID_ARGUMENT); + var normalizedId = projectId.split("\\+"); + if (normalizedId.length == 2 && new UUIDParser().parse(normalizedId[0]).isPresent() && new UUIDParser().parse(normalizedId[1]).isPresent()) { + var baseProject = projectApplicationService.findById(normalizedId[0]).orElse(null); + return new ProjectDTO(projectId, baseProject.name(), baseProject.natures()); + } return this.projectApplicationService.findById(projectId).orElse(null); } } diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/SemanticData.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/SemanticData.java index 82a77e1724..7d781a48a5 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/SemanticData.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/SemanticData.java @@ -165,6 +165,8 @@ public static final class Builder { private Set domains = new LinkedHashSet<>(); + private UUID id; + public Builder project(AggregateReference project) { this.project = Objects.requireNonNull(project); return this; @@ -182,11 +184,16 @@ public Builder domains(Collection domains) { return this; } + public Builder id(UUID id) { + this.id = id; + return this; + } + public SemanticData build(ICause cause) { var semanticData = new SemanticData(); semanticData.isNew = true; - semanticData.id = UUID.randomUUID(); + semanticData.id = Objects.requireNonNullElseGet(this.id, UUID::randomUUID); semanticData.project = Objects.requireNonNull(this.project); semanticData.documents = Objects.requireNonNull(this.documents); semanticData.domains = Objects.requireNonNull(this.domains); diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/repositories/ISemanticDataRepository.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/repositories/ISemanticDataRepository.java index 816554f07d..77d7d8f381 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/repositories/ISemanticDataRepository.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/repositories/ISemanticDataRepository.java @@ -13,7 +13,6 @@ package org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.repositories; import java.util.List; -import java.util.Optional; import java.util.UUID; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; @@ -35,7 +34,7 @@ public interface ISemanticDataRepository extends ListPagingAndSortingRepository< FROM semantic_data semanticData WHERE semanticData.project_id = :projectId """) - Optional findByProjectId(String projectId); + List findByProjectId(String projectId); @Query(""" SELECT semanticData.* diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataCreationService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataCreationService.java index 4e45bf3efd..299c03972f 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataCreationService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataCreationService.java @@ -14,6 +14,7 @@ import java.util.Objects; import java.util.Set; +import java.util.UUID; import org.eclipse.sirius.components.events.ICause; import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; @@ -45,4 +46,14 @@ public void initialize(ICause cause, AggregateReference project .build(cause); this.semanticDataRepository.save(semanticData); } + + @Override + public void add(ICause cause, UUID semanticDataId, AggregateReference project) { + var semanticData = SemanticData.newSemanticData() + .id(semanticDataId) + .project(project) + .documents(Set.of()) + .build(cause); + this.semanticDataRepository.save(semanticData); + } } diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataSearchService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataSearchService.java index 6dcfe65757..c91e62f5f3 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataSearchService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataSearchService.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.UUID; import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; @@ -39,7 +40,12 @@ public SemanticDataSearchService(ISemanticDataRepository semanticDataRepository) @Override public Optional findByProject(AggregateReference project) { - return this.semanticDataRepository.findByProjectId(project.getId()); + return this.semanticDataRepository.findByProjectId(project.getId()).stream().findFirst(); + } + + @Override + public Optional findById(UUID semanticDataId) { + return this.semanticDataRepository.findById(semanticDataId); } @Override diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataUpdateService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataUpdateService.java index c4ec98a156..a79272f184 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataUpdateService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/SemanticDataUpdateService.java @@ -14,13 +14,12 @@ import java.util.Objects; import java.util.Set; +import java.util.UUID; import org.eclipse.sirius.components.events.ICause; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.repositories.ISemanticDataRepository; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataUpdateService; -import org.springframework.data.jdbc.core.mapping.AggregateReference; import org.springframework.stereotype.Service; /** @@ -38,8 +37,8 @@ public SemanticDataUpdateService(ISemanticDataRepository semanticDataRepository) } @Override - public void updateDocuments(ICause cause, AggregateReference project, Set documents, Set domainUris) { - this.semanticDataRepository.findByProjectId(project.getId()).ifPresent(semanticData -> { + public void updateDocuments(ICause cause, UUID semanticDataId, Set documents, Set domainUris) { + this.semanticDataRepository.findById(semanticDataId).ifPresent(semanticData -> { semanticData.updateDocuments(cause, documents, domainUris); this.semanticDataRepository.save(semanticData); }); diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataCreationService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataCreationService.java index 2f72ba52f9..615c290465 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataCreationService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataCreationService.java @@ -16,6 +16,8 @@ import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.springframework.data.jdbc.core.mapping.AggregateReference; +import java.util.UUID; + /** * Used to create the semantic data. * @@ -23,4 +25,6 @@ */ public interface ISemanticDataCreationService { void initialize(ICause cause, AggregateReference project); + + void add(ICause cause, UUID semanticDataId, AggregateReference project); } diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataSearchService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataSearchService.java index 44b22e602f..57383bdf27 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataSearchService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataSearchService.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Optional; +import java.util.UUID; import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; @@ -28,5 +29,7 @@ public interface ISemanticDataSearchService { Optional findByProject(AggregateReference project); + Optional findById(UUID semanticDataId); + List findAllByDomains(List domainUris); } diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataUpdateService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataUpdateService.java index ce3273abb5..09617b7968 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataUpdateService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/semanticdata/services/api/ISemanticDataUpdateService.java @@ -13,11 +13,10 @@ package org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api; import java.util.Set; +import java.util.UUID; import org.eclipse.sirius.components.events.ICause; -import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document; -import org.springframework.data.jdbc.core.mapping.AggregateReference; /** * Used to update the semantic data. @@ -25,5 +24,5 @@ * @author sbegaudeau */ public interface ISemanticDataUpdateService { - void updateDocuments(ICause cause, AggregateReference project, Set documents, Set domainUris); + void updateDocuments(ICause cause, UUID semanticDataId, Set documents, Set domainUris); } diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextInitializer.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextInitializer.java index b266e7108a..f0882a31a2 100644 --- a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextInitializer.java +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextInitializer.java @@ -48,6 +48,8 @@ public void preProcess(IEditingContext editingContext) { .anyMatch(PapayaProjectTemplateProvider.PAPAYA_NATURE::equals)) .isPresent(); + isPapayaProject = isPapayaProject || editingContext.getId().contains("+"); + if (isPapayaProject && editingContext instanceof EditingContext emfEditingContext) { var packageRegistry = emfEditingContext.getDomain().getResourceSet().getPackageRegistry(); packageRegistry.put(PapayaPackage.eNS_URI, PapayaPackage.eINSTANCE); diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextLoader.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextLoader.java new file mode 100644 index 0000000000..181f3e11c9 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextLoader.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2025 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.sirius.web.papaya.services; + +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextLoader; + +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IEditingContextProcessor; +import org.eclipse.sirius.components.core.api.IEditingContextRepresentationDescriptionProvider; +import org.eclipse.sirius.components.emf.services.EditingContextCrossReferenceAdapter; +import org.eclipse.sirius.emfjson.resource.JsonResource; +import org.eclipse.sirius.web.application.UUIDParser; +import org.eclipse.sirius.web.application.editingcontext.EditingContext; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingDomainFactory; +import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceLoader; +import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +/** + * Used to load a Papaya editing context. + * + * @author mcharfadi + */ +@Service +public class PapayaEditingContextLoader implements IEditingContextLoader { + + private static final String DELIMITATOR = "\\+"; + + private final Logger logger = LoggerFactory.getLogger(PapayaEditingContextLoader.class); + + private final ISemanticDataSearchService semanticDataSearchService; + + private final IResourceLoader resourceLoader; + + private final List representationDescriptionProviders; + + private final List editingContextProcessors; + + private final IProjectSearchService projectSearchService; + + private final List migrationParticipantPredicates; + + private final IEditingDomainFactory editingDomainFactory; + + public PapayaEditingContextLoader(ISemanticDataSearchService semanticDataSearchService, IResourceLoader resourceLoader, List representationDescriptionProviders, List editingContextProcessors, IProjectSearchService projectSearchService, List migrationParticipantPredicates, IEditingDomainFactory editingDomainFactory) { + this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService); + this.resourceLoader = Objects.requireNonNull(resourceLoader); + this.representationDescriptionProviders = Objects.requireNonNull(representationDescriptionProviders); + this.editingContextProcessors = Objects.requireNonNull(editingContextProcessors); + this.projectSearchService = Objects.requireNonNull(projectSearchService); + this.migrationParticipantPredicates = Objects.requireNonNull(migrationParticipantPredicates); + this.editingDomainFactory = Objects.requireNonNull(editingDomainFactory); + } + + public IEditingContext load(String editingContextId) { + var normalizedId = editingContextId.split(DELIMITATOR); + var project = this.projectSearchService.findById(normalizedId[0]); + + AdapterFactoryEditingDomain editingDomain = this.editingDomainFactory.createEditingDomain(project.get()); + EditingContext editingContext = new EditingContext(editingContextId, editingDomain, new HashMap<>(), new ArrayList<>()); + + this.toEditingContext(editingContext); + + return editingContext; + } + + public void toEditingContext(EditingContext editingContext) { + this.editingContextProcessors.forEach(processor -> processor.preProcess(editingContext)); + + var normalizedId = editingContext.getId().split(DELIMITATOR); + var optionalSemanticDataId = new UUIDParser().parse(normalizedId[1]); + + if (optionalSemanticDataId.isPresent()) { + var optionalSemanticData = this.semanticDataSearchService.findById(optionalSemanticDataId.get()); + optionalSemanticData.ifPresent(semanticData -> this.loadSemanticData(editingContext, semanticData)); + } + + this.representationDescriptionProviders.forEach(representationDescriptionProvider -> { + var representationDescriptions = representationDescriptionProvider.getRepresentationDescriptions(editingContext); + representationDescriptions.forEach(representationDescription -> editingContext.getRepresentationDescriptions().put(representationDescription.getId(), representationDescription)); + }); + + this.editingContextProcessors.forEach(processor -> processor.postProcess(editingContext)); + } + + private void loadSemanticData(EditingContext editingContext, SemanticData semanticData) { + ResourceSet resourceSet = editingContext.getDomain().getResourceSet(); + resourceSet.getLoadOptions().put(JsonResource.OPTION_SCHEMA_LOCATION, true); + + semanticData.getDocuments().forEach(document -> this.resourceLoader.toResource(resourceSet, document.getId().toString(), document.getName(), document.getContent(), + this.migrationParticipantPredicates.stream().anyMatch(predicate -> predicate.test(editingContext.getId())))); + + // The ECrossReferenceAdapter must be set after the resource loading because it needs to resolve proxies in case + // of inter-resources references + resourceSet.eAdapters().add(new EditingContextCrossReferenceAdapter()); + + this.logger.debug("{} documents loaded for the editing context {}", resourceSet.getResources().size(), editingContext.getId()); + } + + @Override + public boolean canHandle(String projectId) { + var normalizedId = projectId.split(DELIMITATOR); + if (normalizedId.length == 2 && new UUIDParser().parse(normalizedId[0]).isPresent() && new UUIDParser().parse(normalizedId[1]).isPresent()) { + return this.projectSearchService.existsById(normalizedId[0]); + } + return false; + } + +} diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextSaver.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextSaver.java new file mode 100644 index 0000000000..91a9264eef --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/services/PapayaEditingContextSaver.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2025 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.sirius.web.papaya.services; + +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IEditingContextSearchService; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.events.ICause; +import org.eclipse.sirius.web.application.UUIDParser; +import org.eclipse.sirius.web.application.editingcontext.services.EPackageEntry; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextPersistenceFilter; +import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextSaver; +import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceToDocumentService; +import org.eclipse.sirius.web.domain.boundedcontexts.project.Project; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataCreationService; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataSearchService; +import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataUpdateService; +import org.springframework.data.jdbc.core.mapping.AggregateReference; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Used to load a Papaya editing context. + * + * @author mcharfadi + */ +@Service +public class PapayaEditingContextSaver implements IEditingContextSaver { + + private final ISemanticDataUpdateService semanticDataUpdateService; + + private final IResourceToDocumentService resourceToDocumentService; + + private final List persistenceFilters; + + private final List migrationParticipantPredicates; + + private final ISemanticDataCreationService semanticDataCreationService; + + private final ISemanticDataSearchService semanticDataSearchService; + + public PapayaEditingContextSaver(ISemanticDataUpdateService semanticDataUpdateService, IResourceToDocumentService resourceToDocumentService, IEditingContextSearchService editingContextSearchService, List persistenceFilters, List migrationParticipantPredicates, ISemanticDataCreationService semanticDataCreationService, ISemanticDataSearchService semanticDataSearchService) { + this.semanticDataUpdateService = Objects.requireNonNull(semanticDataUpdateService); + this.resourceToDocumentService = Objects.requireNonNull(resourceToDocumentService); + this.persistenceFilters = Objects.requireNonNull(persistenceFilters); + this.migrationParticipantPredicates = Objects.requireNonNull(migrationParticipantPredicates); + this.semanticDataCreationService = Objects.requireNonNull(semanticDataCreationService); + this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService); + } + + @Transactional + @Override + public void save(ICause cause, IEditingContext editingContext) { + if (editingContext instanceof IEMFEditingContext emfEditingContext) { + var applyMigrationParticipants = this.migrationParticipantPredicates.stream().anyMatch(predicate -> predicate.test(emfEditingContext.getId())); + var normalizedId = editingContext.getId().split("\\+"); + AggregateReference projectId = AggregateReference.to(normalizedId[0]); + + var documentData = emfEditingContext.getDomain().getResourceSet().getResources().stream() + .filter(resource -> IEMFEditingContext.RESOURCE_SCHEME.equals(resource.getURI().scheme())) + .filter(resource -> this.persistenceFilters.stream().allMatch(filter -> filter.shouldPersist(resource))) + .map(resource -> this.resourceToDocumentService.toDocument(resource, applyMigrationParticipants)) + .flatMap(Optional::stream) + .collect(Collectors.toSet()); + + var documents = new LinkedHashSet(); + var domainUris = new LinkedHashSet(); + + documentData.forEach(data -> { + documents.add(data.document()); + domainUris.addAll(data.ePackageEntries().stream().map(EPackageEntry::nsURI).toList()); + }); + + var semanticDataUUID = new UUIDParser().parse(normalizedId[1]); + if (semanticDataUUID.isPresent() && semanticDataSearchService.findById(semanticDataUUID.get()).isEmpty()) { + this.semanticDataCreationService.add(cause, semanticDataUUID.get(), projectId); + } + semanticDataUUID.ifPresent(uuid -> this.semanticDataUpdateService.updateDocuments(cause, uuid, documents, domainUris)); + } + } + + @Override + public boolean canHandle(IEditingContext editingContext) { + var normalizedId = editingContext.getId().split("\\+"); + return normalizedId.length == 2 && new UUIDParser().parse(normalizedId[0]).isPresent() && new UUIDParser().parse(normalizedId[1]).isPresent(); + } + +} \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/domain/DomainEventsTest.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/domain/DomainEventsTest.java index 191241cbea..c0372ec264 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/domain/DomainEventsTest.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/domain/DomainEventsTest.java @@ -126,7 +126,7 @@ public void givenDocumentWhenNameModifiedDomainEventPublished() { var newDocumentName = "renamed document"; var updatedDocument = Document.newDocument(originalDocument.getId()).name(newDocumentName).content(originalDocument.getContent()).build(); - this.semanticDataUpdateService.updateDocuments(null, projectId, Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); + this.semanticDataUpdateService.updateDocuments(null, semanticData.getId(), Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); TestTransaction.flagForCommit(); TestTransaction.end(); @@ -154,7 +154,7 @@ public void givenDocumentWhenNameAndContentNotModifiedNoDomainEventPublished() { var newDocumentName = originalDocument.getName(); // Identical to the original except for the timestamps var updatedDocument = Document.newDocument(originalDocument.getId()).name(newDocumentName).content(originalDocument.getContent()).build(); - this.semanticDataUpdateService.updateDocuments(null, projectId, Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); + this.semanticDataUpdateService.updateDocuments(null, semanticData.getId(), Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); TestTransaction.flagForCommit(); TestTransaction.end(); @@ -180,7 +180,7 @@ public void givenDocumentWhenContentModifiedDomainEventPublished() { var originalContent = originalDocument.getContent(); var newContent = originalContent + "modified"; var updatedDocument = Document.newDocument(originalDocument.getId()).name(originalDocument.getName()).content(newContent).build(); - this.semanticDataUpdateService.updateDocuments(null, projectId, Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); + this.semanticDataUpdateService.updateDocuments(null, semanticData.getId(), Set.of(updatedDocument), semanticData.getDomains().stream().map(SemanticDataDomain::uri).collect(Collectors.toSet())); TestTransaction.flagForCommit(); TestTransaction.end();