From 56841eef45492e8f0e6f4d42b5eeda69fafdb62f Mon Sep 17 00:00:00 2001 From: Jerome Gout Date: Mon, 18 Nov 2024 16:46:24 +0100 Subject: [PATCH] [4203] Add a documentation field in Representation metadata Bug: https://github.com/eclipse-sirius/sirius-web/issues/4203 Signed-off-by: Jerome Gout --- CHANGELOG.adoc | 7 + .../collaborative/api/ChangeKind.java | 2 + .../handlers/EditTextfieldEventHandler.java | 2 +- ...ataDetailsViewPageDescriptionProvider.java | 84 ++++++--- ...adataDetailsViewRefreshPolicyProvider.java | 9 +- ...resentationMetadataPersistenceService.java | 1 + .../RepresentationMetadata.java | 25 +++ .../RepresentationMetadataUpdateService.java | 17 ++ .../IRepresentationMetadataUpdateService.java | 3 + ...-representation-metadata-documentation.xml | 31 ++++ .../db/changelog/2025.1/2025.1.0.xml | 1 + ...resentationControllerIntegrationTests.java | 172 +++++++++++++++++- .../src/test/resources/scripts/initialize.sql | 4 + .../src/test/resources/scripts/migration.sql | 4 + 14 files changed, 325 insertions(+), 37 deletions(-) create mode 100644 packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/02-add-representation-metadata-documentation.xml diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 22c32a6bf0..e58f6999de 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -13,6 +13,13 @@ === Deprecation warning +- [form] The project `sirius-components-collecborative-forms` has always decided that any change performed by a widget must be a `ChangeKind.SEMANTIC_CHANGE`. +This decision has created some limit on what can really be edited by a form. +We will soon change this default behavior to use the change kind and parameters returned by the specifier in the `Success` result from the event handler. +As a result, if we consider a textfield widget, in order to keep the existing behavior, specifier will have to create a result with the proper change kind like `new Success(ChangeKind.SEMANTIC_CHANGE, Map.of())` instead of `new Success()`. +If the status returned by the event handler is still something like `new Success()` then, Sirius Components will ignore the change performed by the event handler. +While it can be useful to indicate that nothing has changed in some cases, if it's not done on purpose some representations will not be updated, semantic data may not be saved, etc. +This change will also be propagated over time to other kind of representations. === Breaking changes diff --git a/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/api/ChangeKind.java b/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/api/ChangeKind.java index b8457b4881..230d28b7b3 100644 --- a/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/api/ChangeKind.java +++ b/packages/core/backend/sirius-components-collaborative/src/main/java/org/eclipse/sirius/components/collaborative/api/ChangeKind.java @@ -37,6 +37,8 @@ public final class ChangeKind { public static final String RELOAD_REPRESENTATION = "RELOAD_REPRESENTATION"; + public static final String REPRESENTATION_METADATA_UPDATE = "REPRESENTATION_METADATA_UPDATE"; + private ChangeKind() { // Prevent instantiation } diff --git a/packages/forms/backend/sirius-components-collaborative-forms/src/main/java/org/eclipse/sirius/components/collaborative/forms/handlers/EditTextfieldEventHandler.java b/packages/forms/backend/sirius-components-collaborative-forms/src/main/java/org/eclipse/sirius/components/collaborative/forms/handlers/EditTextfieldEventHandler.java index 2bca5130dd..9f2443df5a 100644 --- a/packages/forms/backend/sirius-components-collaborative-forms/src/main/java/org/eclipse/sirius/components/collaborative/forms/handlers/EditTextfieldEventHandler.java +++ b/packages/forms/backend/sirius-components-collaborative-forms/src/main/java/org/eclipse/sirius/components/collaborative/forms/handlers/EditTextfieldEventHandler.java @@ -101,7 +101,7 @@ public void handle(One payloadSink, Many changeDesc } if (status instanceof Success success) { payload = new SuccessPayload(formInput.id(), success.getMessages()); - if (success.getChangeKind() == null || success.getChangeKind().isBlank()) { + if (success.getChangeKind().isBlank()) { changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, formInput.representationId(), formInput); } else { changeDescription = new ChangeDescription(success.getChangeKind(), formInput.representationId(), formInput, success.getParameters()); diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewPageDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewPageDescriptionProvider.java index fac9158bf1..420cc63cac 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewPageDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewPageDescriptionProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024 CEA LIST. * 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 @@ -13,7 +13,6 @@ package org.eclipse.sirius.web.application.representation.services; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,6 +33,7 @@ import org.eclipse.sirius.components.forms.description.AbstractControlDescription; import org.eclipse.sirius.components.forms.description.GroupDescription; import org.eclipse.sirius.components.forms.description.PageDescription; +import org.eclipse.sirius.components.forms.description.TextareaDescription; import org.eclipse.sirius.components.forms.description.TextfieldDescription; import org.eclipse.sirius.components.representations.Failure; import org.eclipse.sirius.components.representations.IStatus; @@ -82,31 +82,18 @@ private PageDescription getPageDescription() { } private List createControlDescriptions() { - List controls = new ArrayList<>(); - - BiFunction representationLabelWriter = (metadata, newLabel) -> { - // delegate the renaming of the representation to EditingContextEventProcessor - var representationId = ((RepresentationMetadata) metadata).getId(); - Map parameters = new HashMap<>(); - parameters.put(EditingContextEventProcessor.REPRESENTATION_ID, representationId.toString()); - parameters.put(EditingContextEventProcessor.REPRESENTATION_LABEL, newLabel); - return new Success(ChangeKind.REPRESENTATION_TO_RENAME, parameters); - }; + var labelControl = this.createLabelTextField(); + var documentationControl = this.createDocumentationTextArea(); - var label = this.createTextField("metadata.label", "Label", - metadata -> ((RepresentationMetadata) metadata).getLabel(), - representationLabelWriter); - controls.add(label); - return controls; + return List.of(labelControl, documentationControl); } - private GroupDescription createGroupDescription(List controls) { Function> semanticElementsProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class).stream().toList(); return GroupDescription.newGroupDescription("group") .idProvider(variableManager -> "group") - .labelProvider(variableManager -> "Core properties") + .labelProvider(variableManager -> "Core Properties") .semanticElementsProvider(semanticElementsProvider) .controlDescriptions(controls) .build(); @@ -127,28 +114,67 @@ private PageDescription createPageDescription(String id, GroupDescription groupD .build(); } - private TextfieldDescription createTextField(String id, String title, Function reader, BiFunction writer) { - Function valueProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(reader) + private TextfieldDescription createLabelTextField() { + Function valueProvider = variableManager -> variableManager.get(VariableManager.SELF, RepresentationMetadata.class) + .map(RepresentationMetadata::getLabel) + .orElse(""); + + BiFunction newValueHandler = (variableManager, newValue) -> { + var self = variableManager.get(VariableManager.SELF, RepresentationMetadata.class); + if (self.isPresent()) { + var representationMetadata = self.get(); + + // delegate the renaming of the representation to EditingContextEventProcessor + Map parameters = new HashMap<>(); + parameters.put(EditingContextEventProcessor.REPRESENTATION_ID, representationMetadata.getId().toString()); + parameters.put(EditingContextEventProcessor.REPRESENTATION_LABEL, newValue); + return new Success(ChangeKind.REPRESENTATION_TO_RENAME, parameters); + } + return new Failure(""); + }; + + Function semanticTargetIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.identityService::getId) + .orElse(null); + + return TextfieldDescription.newTextfieldDescription("metadata.label") + .idProvider(variableManager -> "metadata.label") + .targetObjectIdProvider(semanticTargetIdProvider) + .labelProvider(variableManager -> "Label") + .valueProvider(valueProvider) + .newValueHandler(newValueHandler) + .diagnosticsProvider(variableManager -> List.of()) + .kindProvider(this::kindProvider) + .messageProvider(this::messageProvider) + .isReadOnlyProvider(variableManager -> false) + .build(); + } + + private TextareaDescription createDocumentationTextArea() { + Function valueProvider = variableManager -> variableManager.get(VariableManager.SELF, RepresentationMetadata.class) + .map(RepresentationMetadata::getDocumentation) .orElse(""); BiFunction newValueHandler = (variableManager, newValue) -> { - var self = variableManager.get(VariableManager.SELF, Object.class); + var self = variableManager.get(VariableManager.SELF, RepresentationMetadata.class); if (self.isPresent()) { - return writer.apply(self.get(), newValue); - } else { - return new Failure(""); + var representationMetadata = self.get(); + return Optional.of(this.representationMetadataUpdateService.updateDocumentation(null, representationMetadata.getId(), newValue)) + .filter(org.eclipse.sirius.web.domain.services.Success.class::isInstance) + .map(success -> (IStatus) new Success(ChangeKind.REPRESENTATION_METADATA_UPDATE, Map.of())) + .orElseGet(() -> new Failure("")); } + return new Failure(""); }; Function semanticTargetIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) .map(this.identityService::getId) .orElse(null); - return org.eclipse.sirius.components.forms.description.TextfieldDescription.newTextfieldDescription(id) - .idProvider(variableManager -> id) + return TextareaDescription.newTextareaDescription("metadata.documentation") + .idProvider(variableManager -> "metadata.documentation") .targetObjectIdProvider(semanticTargetIdProvider) - .labelProvider(variableManager -> title) + .labelProvider(variableManager -> "Documentation") .valueProvider(valueProvider) .newValueHandler(newValueHandler) .diagnosticsProvider(variableManager -> List.of()) diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewRefreshPolicyProvider.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewRefreshPolicyProvider.java index ae6a0a4982..5d4c434e99 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewRefreshPolicyProvider.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataDetailsViewRefreshPolicyProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024 CEA LIST. * 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 @@ -17,13 +17,12 @@ import org.eclipse.sirius.components.collaborative.api.ChangeKind; import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicy; import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyProvider; -import org.eclipse.sirius.components.collaborative.portals.PortalChangeKind; import org.eclipse.sirius.components.representations.IRepresentationDescription; import org.eclipse.sirius.web.application.views.details.services.PropertiesEventProcessorFactory; import org.springframework.stereotype.Service; /** - * The representation refresh policy provider for the representation representations. + * The representation refresh policy provider for the representation metadata properties page. * * @author Jerome Gout */ @@ -36,9 +35,7 @@ public class RepresentationMetadataDetailsViewRefreshPolicyProvider implements I ChangeKind.REPRESENTATION_RENAMING, ChangeKind.REPRESENTATION_TO_DELETE, ChangeKind.REPRESENTATION_METADATA_UPDATE, - ChangeKind.SEMANTIC_CHANGE, - PortalChangeKind.PORTAL_VIEW_ADDITION.name(), - PortalChangeKind.PORTAL_VIEW_REMOVAL.name()); + ChangeKind.SEMANTIC_CHANGE); @Override diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataPersistenceService.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataPersistenceService.java index d627a75e1f..883779cb73 100644 --- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataPersistenceService.java +++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/representation/services/RepresentationMetadataPersistenceService.java @@ -58,6 +58,7 @@ public void save(ICause cause, IEditingContext editingContext, org.eclipse.siriu .iconURLs(representationMetadata.iconURLs().stream() .map(RepresentationIconURL::new) .toList()) + .documentation("") .build(cause); this.representationMetadataCreationService.create(boundedRepresentationMetadata); diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/RepresentationMetadata.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/RepresentationMetadata.java index 053c9baa63..d3b651326a 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/RepresentationMetadata.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/RepresentationMetadata.java @@ -65,6 +65,8 @@ public class RepresentationMetadata extends AbstractValidatingAggregateRoot iconURLs = new ArrayList<>(); + private String documentation; + @Override public UUID getId() { return this.id; @@ -113,6 +115,21 @@ public List getIconURLs() { return Collections.unmodifiableList(this.iconURLs); } + public String getDocumentation() { + return this.documentation; + } + + public void updateDocumentation(ICause cause, String newDocumentation) { + if (this.documentation.isEmpty() || !this.documentation.equals(newDocumentation)) { + this.documentation = newDocumentation; + + var now = Instant.now(); + this.lastModifiedOn = now; + + this.registerEvent(new RepresentationMetadataUpdatedEvent(UUID.randomUUID(), now, cause, this)); + } + } + public void dispose(ICause cause) { this.registerEvent(new RepresentationMetadataDeletedEvent(UUID.randomUUID(), Instant.now(), cause, this)); } @@ -148,6 +165,8 @@ public static final class Builder { private List iconURLs; + private String documentation; + public Builder(UUID id) { this.id = Objects.requireNonNull(id); } @@ -182,6 +201,11 @@ public Builder iconURLs(List iconURLs) { return this; } + public Builder documentation(String documentation) { + this.documentation = Objects.requireNonNull(documentation); + return this; + } + public RepresentationMetadata build(ICause cause) { var representationMetadata = new RepresentationMetadata(); representationMetadata.isNew = true; @@ -192,6 +216,7 @@ public RepresentationMetadata build(ICause cause) { representationMetadata.label = Objects.requireNonNull(this.label); representationMetadata.kind = Objects.requireNonNull(this.kind); representationMetadata.iconURLs = Objects.requireNonNull(this.iconURLs); + representationMetadata.documentation = Objects.requireNonNull(this.documentation); var now = Instant.now(); representationMetadata.createdOn = now; diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/RepresentationMetadataUpdateService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/RepresentationMetadataUpdateService.java index ae55a24e62..4a44f81759 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/RepresentationMetadataUpdateService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/RepresentationMetadataUpdateService.java @@ -16,6 +16,7 @@ import java.util.UUID; import org.eclipse.sirius.components.events.ICause; +import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.repositories.IRepresentationMetadataRepository; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataUpdateService; import org.eclipse.sirius.web.domain.services.Failure; @@ -56,4 +57,20 @@ public IResult updateLabel(ICause cause, UUID representationMetadataId, St return result; } + + @Override + public IResult updateDocumentation(ICause cause, UUID representationMetadataId, String documentation) { + IResult result = new Failure<>(this.messageService.notFound()); + + var optionalRepresentationMetadata = this.representationMetadataRepository.findMetadataById(representationMetadataId); + if (optionalRepresentationMetadata.isPresent()) { + var representationMetadata = optionalRepresentationMetadata.get(); + representationMetadata.updateDocumentation(cause, documentation); + this.representationMetadataRepository.save(representationMetadata); + + result = new Success<>(representationMetadata); + } + + return result; + } } diff --git a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/api/IRepresentationMetadataUpdateService.java b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/api/IRepresentationMetadataUpdateService.java index 7aeecd1e6e..3cee47af4c 100644 --- a/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/api/IRepresentationMetadataUpdateService.java +++ b/packages/sirius-web/backend/sirius-web-domain/src/main/java/org/eclipse/sirius/web/domain/boundedcontexts/representationdata/services/api/IRepresentationMetadataUpdateService.java @@ -15,6 +15,7 @@ import java.util.UUID; import org.eclipse.sirius.components.events.ICause; +import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; import org.eclipse.sirius.web.domain.services.IResult; /** @@ -25,4 +26,6 @@ public interface IRepresentationMetadataUpdateService { IResult updateLabel(ICause cause, UUID representationMetadataId, String label); + + IResult updateDocumentation(ICause cause, UUID representationMetadataId, String documentation); } diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/02-add-representation-metadata-documentation.xml b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/02-add-representation-metadata-documentation.xml new file mode 100644 index 0000000000..8b0c1a5415 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/02-add-representation-metadata-documentation.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + UPDATE representation_metadata + SET documentation = '' + + + + + \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/2025.1.0.xml b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/2025.1.0.xml index 70b12da41e..607a40443c 100644 --- a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/2025.1.0.xml +++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/resources/db/changelog/2025.1/2025.1.0.xml @@ -16,4 +16,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd"> + diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/representations/RepresentationControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/representations/RepresentationControllerIntegrationTests.java index 94bc80f241..c04063a479 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/representations/RepresentationControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/representations/RepresentationControllerIntegrationTests.java @@ -13,19 +13,39 @@ package org.eclipse.sirius.web.application.controllers.representations; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import com.jayway.jsonpath.JsonPath; +import java.time.Duration; import java.util.List; import java.util.Map; - +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import org.eclipse.sirius.components.collaborative.forms.dto.EditTextfieldInput; +import org.eclipse.sirius.components.collaborative.forms.dto.FormRefreshedEventPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.forms.Form; +import org.eclipse.sirius.components.forms.Textarea; +import org.eclipse.sirius.components.forms.Textfield; +import org.eclipse.sirius.components.forms.tests.assertions.FormAssertions; +import org.eclipse.sirius.components.forms.tests.graphql.EditTextfieldMutationRunner; +import org.eclipse.sirius.components.forms.tests.navigation.FormNavigator; import org.eclipse.sirius.components.graphql.tests.RepresentationDescriptionsQueryRunner; import org.eclipse.sirius.web.AbstractIntegrationTests; +import org.eclipse.sirius.web.application.views.details.dto.DetailsEventInput; import org.eclipse.sirius.web.data.TestIdentifiers; import org.eclipse.sirius.web.services.api.IDomainEventCollector; +import org.eclipse.sirius.web.tests.graphql.DetailsEventSubscriptionRunner; import org.eclipse.sirius.web.tests.graphql.RepresentationMetadataQueryRunner; import org.eclipse.sirius.web.tests.graphql.RepresentationsMetadataQueryRunner; +import org.eclipse.sirius.web.tests.services.api.IGivenCommittedTransaction; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.eclipse.sirius.web.tests.services.representation.RepresentationIdBuilder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -35,6 +55,9 @@ import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.transaction.annotation.Transactional; +import graphql.execution.DataFetcherResult; +import reactor.test.StepVerifier; + /** * Integration tests of the representation controllers. * @@ -60,6 +83,18 @@ public class RepresentationControllerIntegrationTests extends AbstractIntegratio @Autowired private IDomainEventCollector domainEventCollector; + @Autowired + private RepresentationIdBuilder representationIdBuilder; + + @Autowired + private IGivenCommittedTransaction givenCommittedTransaction; + + @Autowired + private DetailsEventSubscriptionRunner detailsEventSubscriptionRunner; + + @Autowired + private EditTextfieldMutationRunner editTextfieldMutationRunner; + @BeforeEach public void beforeEach() { this.domainEventCollector.clear(); @@ -210,4 +245,139 @@ public void givenObjectIdWhenQueryIsPerformedThenAllTheRepresentationDescription List representationIds = JsonPath.read(result, "$.data.viewer.editingContext.representationDescriptions.edges[*].node.id"); assertThat(representationIds).hasSize(3); } + + @Test + @DisplayName("Given a Portal representation, when we subscribe to its properties events, then the form is sent") + @Sql(scripts = { "/scripts/initialize.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenPortalRepresentationWhenWeSubscribeToItsPropertiesEventsThenTheFormIsSent() { + var detailsRepresentationId = this.representationIdBuilder.buildDetailsRepresentationId(List.of(TestIdentifiers.EPACKAGE_PORTAL_REPRESENTATION.toString())); + var input = new DetailsEventInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), detailsRepresentationId); + var flux = this.detailsEventSubscriptionRunner.run(input); + + Predicate
formPredicate = form -> { + var groupNavigator = new FormNavigator(form).page("Portal").group("Core Properties"); + + var labelTextField = groupNavigator.findWidget("Label", Textfield.class); + FormAssertions.assertThat(labelTextField).isNotNull(); + FormAssertions.assertThat(labelTextField.getValue()).isEqualTo("Portal"); + + var documentationTextArea = groupNavigator.findWidget("Documentation", Textarea.class); + FormAssertions.assertThat(documentationTextArea).isNotNull(); + FormAssertions.assertThat(documentationTextArea.getValue()).isEqualTo("documentation"); + + return true; + }; + + Predicate formContentMatcher = object -> Optional.of(object) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(FormRefreshedEventPayload.class::isInstance) + .map(FormRefreshedEventPayload.class::cast) + .map(FormRefreshedEventPayload::form) + .filter(formPredicate) + .isPresent(); + + StepVerifier.create(flux) + .expectNextMatches(formContentMatcher) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @Test + @DisplayName("Given a Portal representation, when we edit its label in the Details view, then the label is changed.") + @Sql(scripts = { "/scripts/initialize.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenPortalRepresentationWhenWeEditItsLabelInTheDetailsViewThenTheLabelIsChanged() { + this.givenCommittedTransaction.commit(); + + var detailsRepresentationId = this.representationIdBuilder.buildDetailsRepresentationId(List.of(TestIdentifiers.EPACKAGE_PORTAL_REPRESENTATION.toString())); + var detailsEventInput = new DetailsEventInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), detailsRepresentationId); + var flux = this.detailsEventSubscriptionRunner.run(detailsEventInput); + + var formId = new AtomicReference(); + + Consumer initialFormContentConsumer = this.getFormConsumer(form -> { + formId.set(form.getId()); + }); + + Runnable editLabel = () -> { + var editLabelInput = new EditTextfieldInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), formId.get(), "metadata.label", "NewPortal"); + var result = this.editTextfieldMutationRunner.run(editLabelInput); + + String typename = JsonPath.read(result, "$.data.editTextfield.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + }; + + Consumer updateFormLabel = this.getFormConsumer(form -> { + var groupNavigator = new FormNavigator(form).page("NewPortal").group("Core Properties"); + + var labelTextfield = groupNavigator.findWidget("Label", Textfield.class); + FormAssertions.assertThat(labelTextfield).isNotNull(); + FormAssertions.assertThat(labelTextfield.getValue()).isEqualTo("NewPortal"); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialFormContentConsumer) + .then(editLabel) + .consumeNextWith(updateFormLabel) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @Test + @DisplayName("Given a Portal representation, when we edit its documentation in the Details view, then the documentation is changed.") + @Sql(scripts = { "/scripts/initialize.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenPortalRepresentationWhenWeEditItsDocumentationInTheDetailsViewThenTheDocumentationIsChanged() { + + this.givenCommittedTransaction.commit(); + + var detailsRepresentationId = this.representationIdBuilder.buildDetailsRepresentationId(List.of(TestIdentifiers.EPACKAGE_PORTAL_REPRESENTATION.toString())); + var detailsEventInput = new DetailsEventInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), detailsRepresentationId); + var flux = this.detailsEventSubscriptionRunner.run(detailsEventInput); + + var formId = new AtomicReference(); + + Consumer initialFormContentConsumer = this.getFormConsumer(form -> { + formId.set(form.getId()); + }); + + Runnable editDocumentation = () -> { + var editDocumentationInput = new EditTextfieldInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), formId.get(), "metadata.documentation", "This is a documentation"); + var result = this.editTextfieldMutationRunner.run(editDocumentationInput); + + String typename = JsonPath.read(result, "$.data.editTextfield.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + }; + + Consumer updateFormDocumentation = this.getFormConsumer(form -> { + var groupNavigator = new FormNavigator(form).page("Portal").group("Core Properties"); + + var documentationTextArea = groupNavigator.findWidget("Documentation", Textarea.class); + FormAssertions.assertThat(documentationTextArea).isNotNull(); + FormAssertions.assertThat(documentationTextArea.getValue()).isEqualTo("This is a documentation"); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialFormContentConsumer) + .then(editDocumentation) + .consumeNextWith(updateFormDocumentation) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + private Consumer getFormConsumer(Consumer formConsumer) { + return object -> Optional.of(object) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(FormRefreshedEventPayload.class::isInstance) + .map(FormRefreshedEventPayload.class::cast) + .map(FormRefreshedEventPayload::form) + .ifPresentOrElse(formConsumer, () -> fail("Missing form")); + + } + } diff --git a/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/initialize.sql b/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/initialize.sql index 4f2a424159..2533588f90 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/initialize.sql +++ b/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/initialize.sql @@ -162,6 +162,7 @@ INSERT INTO representation_metadata ( target_object_id, description_id, label, + documentation, kind, created_on, last_modified_on @@ -171,6 +172,7 @@ INSERT INTO representation_metadata ( '3237b215-ae23-48d7-861e-f542a4b9a4b8', '69030a1b-0b5f-3c1d-8399-8ca260e4a672', 'Portal', + 'documentation', 'siriusComponents://representation?type=Portal', '2024-01-01 9:42:0.000', '2024-01-02 9:42:0.000' @@ -225,6 +227,7 @@ INSERT INTO representation_metadata ( target_object_id, description_id, label, + documentation, kind, created_on, last_modified_on @@ -234,6 +237,7 @@ INSERT INTO representation_metadata ( '3237b215-ae23-48d7-861e-f542a4b9a4b8', '69030a1b-0b5f-3c1d-8399-8ca260e4a672', 'Portal', + '', 'siriusComponents://representation?type=Portal', '2024-01-01 9:42:0.000', '2024-01-02 9:42:0.000' diff --git a/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/migration.sql b/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/migration.sql index 9f9c5de4bc..135091b9b6 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/migration.sql +++ b/packages/sirius-web/backend/sirius-web/src/test/resources/scripts/migration.sql @@ -269,6 +269,7 @@ INSERT INTO representation_metadata ( target_object_id, description_id, label, + documentation, kind, created_on, last_modified_on @@ -278,6 +279,7 @@ INSERT INTO representation_metadata ( '719d2b8f-ab70-438d-a999-306de10654a7', 'siriusComponents://representationDescription?kind=TreeMap', 'Hierarchy Migration', + '', 'siriusComponents://representation?type=TreeMap', '2024-01-01 9:42:0.000', '2024-01-02 9:42:0.000' @@ -460,6 +462,7 @@ INSERT INTO representation_metadata ( target_object_id, description_id, label, + documentation, kind, created_on, last_modified_on @@ -469,6 +472,7 @@ INSERT INTO representation_metadata ( '79752a18-c7d8-41c0-8a27-a79ea9de09d8', 'siriusComponents://representationDescription?kind=diagramDescription&sourceKind=view&sourceId=c5857f07-7382-3215-8c53-b690ca983655&sourceElementId=e932123d-b916-3537-84d2-86a4f5873d93', 'Diagram UserResizable Migration', + '', 'siriusComponents://representation?type=Diagram', '2024-07-04 9:42:0.000', '2024-07-04 9:42:0.000'