Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2456, 2466, 2469, 2470] Fixes on reference widget #2478

Merged
merged 5 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,21 @@ UI is unchanged except that we removed the transactional behavior of the _Transf
A new parameter `rootDomainsOnly: Boolean` has been added, allowing to remove domains not compatible with root objects.
For example, Diagram & Form domains are not root domains because `DiagramDescription` and `FormDescription` are not root objects valid candidates.
- https://github.com/eclipse-sirius/sirius-web/issues/1712[#1712] [diagram] The GraphQL type `Node` must now implement `isLabelEditable` boolean field.
- https://github.com/eclipse-sirius/sirius-web/issues/2456[#2456] [sirius-web] The GraphQL field `CreateChildSuccessPayload` now expects to have a list of messages.

=== Dependency update


=== Bug fixes

- https://github.com/eclipse-sirius/sirius-web/issues/2360[#2360] [form] Fix an issue that prevent creation of new object on some containment reference with the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2456[#2456] [form] Fix an issue that prevent creation of new object on mono valued and containment reference when a value is already set with the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2360[#2360] [form] Fix an issue that prevents creation of new object on some containment reference with the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2456[#2456] [form] Fix an issue that prevents creation of new object on mono valued and containment reference when a value is already set with the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2269[#2269] [view] Fix an issue where it was possible to create a DiagramDescription or a FormDescription as a root object of a resource.
- https://github.com/eclipse-sirius/sirius-web/issues/2470[#2470] [form] Fix an issue that prevents the removal of a contained reference from the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2469[#2469] [form] Fix an issue that occurs when navigating from a reference widget to a filtered item in the explorer.
- https://github.com/eclipse-sirius/sirius-web/issues/2466[#2466] [form] Fix an issue that prevents the selection of an ancestor in the reference widget.
- https://github.com/eclipse-sirius/sirius-web/issues/2456[#2456] [form] Add a more meaningful message when trying to add a new value to a mono reference not empty.
- https://github.com/eclipse-sirius/sirius-web/issues/2487[#2487] [form] Fix an issue that prevents an error when using values not in the option list in a reference widget modal transfert.
- https://github.com/eclipse-sirius/sirius-web/issues/1712[#1712] [diagram] Fix an issue that triggers direct label edition even if there is no corresponding tool.
Note that double-clicking no longer triggers a direct edit.

Expand Down
253 changes: 141 additions & 112 deletions integration-tests/cypress/e2e/project/edit/widget-reference.cy.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion integration-tests/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
* Copyright (c) 2021, 2023 Obeo.
* This program and the accompanying materials
* are made available under the erms of the Eclipse Public License v2.0
* 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/
*
Expand All @@ -13,5 +13,6 @@

import 'cypress-file-upload';

import './explorerCommands';
import './serverCommands';
import './testIdCommands';
30 changes: 30 additions & 0 deletions integration-tests/cypress/support/explorerCommands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2023 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
*******************************************************************************/

Cypress.Commands.add('createChildObject', (parent, objectType) => {
cy.getByTestId(`${parent}-more`).click();
cy.getByTestId('new-object').click();
cy.getByTestId('childCreationDescription').children('[role="button"]').invoke('text').should('have.length.gt', 1);
cy.getByTestId('childCreationDescription').click();
cy.getByTestId('childCreationDescription').get(`[data-value="${objectType}"]`).should('exist').click();
cy.getByTestId('create-object').click();
});

Cypress.Commands.add('createRepresentationFromExplorer', (parent, representationType) => {
cy.getByTestId(`${parent}-more`).click();
cy.getByTestId('treeitem-contextmenu').findByTestId('new-representation').click();
cy.getByTestId('representationDescription').children('[role="button"]').invoke('text').should('have.length.gt', 1);
cy.getByTestId('representationDescription').click();
cy.getByTestId(representationType).should('exist').click();
cy.getByTestId('create-representation').click();
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
*/
public class EStructuralFeatureChoiceOfValueProvider implements Function<VariableManager, List<?>> {

private String featureVariableName;
private final String featureVariableName;

private AdapterFactory adapterFactory;
private final AdapterFactory adapterFactory;

public EStructuralFeatureChoiceOfValueProvider(String featureVariableName, AdapterFactory adapterFactory) {
this.featureVariableName = Objects.requireNonNull(featureVariableName);
Expand All @@ -50,15 +50,12 @@ public List<?> apply(VariableManager variableManager) {
EReference eReference = optionalEReference.get();

Object adapter = this.adapterFactory.adapt(eObject, IItemPropertySource.class);
if (adapter instanceof IItemPropertySource) {
IItemPropertySource itemPropertySource = (IItemPropertySource) adapter;
if (adapter instanceof IItemPropertySource itemPropertySource) {
IItemPropertyDescriptor descriptor = itemPropertySource.getPropertyDescriptor(eObject, eReference);
if (descriptor != null) {
// @formatter:off
return descriptor.getChoiceOfValues(eObject).stream()
.filter(Objects::nonNull)
.toList();
// @formatter:on
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.dto;

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

import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.representations.Message;

/**
* The payload of the create child mutation.
*
* @author sbegaudeau
*/
public record CreateChildSuccessPayload(UUID id, Object object) implements IPayload {
public record CreateChildSuccessPayload(UUID id, Object object, List<Message> messages) implements IPayload {

public CreateChildSuccessPayload {
Objects.requireNonNull(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.handlers;

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

Expand All @@ -25,9 +26,12 @@
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IEditService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IFeedbackMessageService;
import org.eclipse.sirius.components.core.api.IInput;
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.representations.Message;
import org.eclipse.sirius.components.representations.MessageLevel;
import org.springframework.stereotype.Service;

import io.micrometer.core.instrument.Counter;
Expand All @@ -49,18 +53,19 @@ public class CreateChildEventHandler implements IEditingContextEventHandler {

private final ICollaborativeMessageService messageService;

private final IFeedbackMessageService feedbackMessageService;

private final Counter counter;

public CreateChildEventHandler(IObjectService objectService, IEditService editService, ICollaborativeMessageService messageService, MeterRegistry meterRegistry) {
public CreateChildEventHandler(IObjectService objectService, IEditService editService, ICollaborativeMessageService messageService, IFeedbackMessageService feedbackMessageService, MeterRegistry meterRegistry) {
this.objectService = Objects.requireNonNull(objectService);
this.editService = Objects.requireNonNull(editService);
this.messageService = Objects.requireNonNull(messageService);
this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService);

// @formatter:off
this.counter = Counter.builder(Monitoring.EVENT_HANDLER)
.tag(Monitoring.NAME, this.getClass().getSimpleName())
.register(meterRegistry);
// @formatter:on
}

@Override
Expand All @@ -72,29 +77,32 @@ public boolean canHandle(IEditingContext editingContext, IInput input) {
public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, IInput input) {
this.counter.increment();

String message = this.messageService.invalidInput(input.getClass().getSimpleName(), CreateChildInput.class.getSimpleName());
List<Message> messages = List.of(new Message(this.messageService.invalidInput(input.getClass().getSimpleName(), CreateChildInput.class.getSimpleName()),
MessageLevel.ERROR));
ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, editingContext.getId(), input);
IPayload payload = null;

if (input instanceof CreateChildInput) {
CreateChildInput createChildInput = (CreateChildInput) input;
if (input instanceof CreateChildInput createChildInput) {
String parentObjectId = createChildInput.objectId();
String childCreationDescriptionId = createChildInput.childCreationDescriptionId();

Optional<Object> createdChildOptional = this.objectService.getObject(editingContext, parentObjectId).flatMap(parent -> {
return this.editService.createChild(editingContext, parent, childCreationDescriptionId);
});
Optional<Object> createdChildOptional = this.objectService.getObject(editingContext, parentObjectId)
.flatMap(parent -> this.editService.createChild(editingContext, parent, childCreationDescriptionId));

if (createdChildOptional.isPresent()) {
payload = new CreateChildSuccessPayload(input.id(), createdChildOptional.get());
payload = new CreateChildSuccessPayload(input.id(), createdChildOptional.get(), this.feedbackMessageService.getFeedbackMessages());
changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, editingContext.getId(), input);
} else {
message = this.messageService.objectCreationFailed();
if (this.feedbackMessageService.getFeedbackMessages().isEmpty()) {
messages = List.of(new Message(this.messageService.objectCreationFailed(), MessageLevel.ERROR));
} else {
messages = this.feedbackMessageService.getFeedbackMessages();
}
}
}

if (payload == null) {
payload = new ErrorPayload(input.id(), message);
payload = new ErrorPayload(input.id(), messages);
}

payloadSink.tryEmitValue(payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ union CreateChildPayload = ErrorPayload|CreateChildSuccessPayload
type CreateChildSuccessPayload {
id: ID!
object: Object
messages: [Message]!
}

input CreateRepresentationInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IEditService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IFeedbackMessageService;
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.core.api.IPayload;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -93,7 +94,7 @@ public Optional<Object> getObject(IEditingContext editingContext, String objectI
}
};

CreateChildEventHandler handler = new CreateChildEventHandler(objectService, editService, new ICollaborativeMessageService.NoOp(), new SimpleMeterRegistry());
CreateChildEventHandler handler = new CreateChildEventHandler(objectService, editService, new ICollaborativeMessageService.NoOp(), new IFeedbackMessageService.NoOp(), new SimpleMeterRegistry());
var input = new CreateChildInput(UUID.randomUUID(), UUID.randomUUID().toString(), "parentObjectId", "childCreationDescriptionId");

IEditingContext editingContext = () -> UUID.randomUUID().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
Expand All @@ -44,8 +45,12 @@
import org.eclipse.sirius.components.core.api.ChildCreationDescription;
import org.eclipse.sirius.components.core.api.IEditService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IFeedbackMessageService;
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.emf.services.api.IEMFKindService;
import org.eclipse.sirius.components.emf.services.messages.IEMFMessageService;
import org.eclipse.sirius.components.representations.Message;
import org.eclipse.sirius.components.representations.MessageLevel;
import org.eclipse.sirius.emfjson.resource.JsonResourceImpl;
import org.springframework.stereotype.Service;

Expand All @@ -66,11 +71,17 @@ public class EditService implements IEditService {

private final IObjectService objectService;

public EditService(IEMFKindService emfKindService, ComposedAdapterFactory composedAdapterFactory, ISuggestedRootObjectTypesProvider suggestedRootObjectsProvider, IObjectService objectService) {
private final IFeedbackMessageService feedbackMessageService;

private final IEMFMessageService messageService;

public EditService(IEMFKindService emfKindService, ComposedAdapterFactory composedAdapterFactory, ISuggestedRootObjectTypesProvider suggestedRootObjectsProvider, IObjectService objectService, IFeedbackMessageService feedbackMessageService, IEMFMessageService messageService) {
this.emfKindService = Objects.requireNonNull(emfKindService);
this.composedAdapterFactory = Objects.requireNonNull(composedAdapterFactory);
this.suggestedRootObjectTypesProvider = Objects.requireNonNull(suggestedRootObjectsProvider);
this.objectService = Objects.requireNonNull(objectService);
this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService);
this.messageService = Objects.requireNonNull(messageService);
}

private Optional<EClass> getEClass(EPackage.Registry ePackageRegistry, String kind) {
Expand Down Expand Up @@ -149,7 +160,6 @@ public List<ChildCreationDescription> getChildCreationDescriptions(IEditingConte

@Override
public Optional<Object> createChild(IEditingContext editingContext, Object object, String childCreationDescriptionId) {
// @formatter:off
var optionalEditingDomain = Optional.of(editingContext)
.filter(EditingContext.class::isInstance)
.map(EditingContext.class::cast)
Expand All @@ -158,20 +168,17 @@ public Optional<Object> createChild(IEditingContext editingContext, Object objec
Optional<EObject> optionalEObject = Optional.of(object)
.filter(EObject.class::isInstance)
.map(EObject.class::cast);
// @formatter:on

if (optionalEditingDomain.isPresent() && optionalEObject.isPresent()) {
AdapterFactoryEditingDomain editingDomain = optionalEditingDomain.get();
EObject eObject = optionalEObject.get();

Collection<?> newChildDescriptors = editingDomain.getNewChildDescriptors(eObject, null);

// @formatter:off
List<CommandParameter> commandParameters = newChildDescriptors.stream()
.filter(CommandParameter.class::isInstance)
.map(CommandParameter.class::cast)
.toList();
// @formatter:on

Adapter adapter = editingDomain.getAdapterFactory().adapt(eObject, IEditingDomainItemProvider.class);
if (adapter instanceof IEditingDomainItemProvider editingDomainItemProvider) {
Expand All @@ -191,6 +198,14 @@ public Optional<Object> createChild(IEditingContext editingContext, Object objec

private Optional<Object> createObject(AdapterFactoryEditingDomain editingDomain, EObject eObject, CommandParameter commandParameter) {
Optional<Object> objectOptional = Optional.empty();

if (commandParameter.getEStructuralFeature() instanceof EReference ref && ref.isContainment() && !ref.isMany() && eObject.eGet(ref) != null) {
this.feedbackMessageService
.addFeedbackMessage(new Message(this.messageService.upperBoundaryReached(commandParameter.getEValue().eClass()
.getName(), commandParameter.getEStructuralFeature().getName()), MessageLevel.WARNING));
return Optional.empty();
}

Command createChildCommand = CreateChildCommand.create(editingDomain, eObject, commandParameter, Collections.singletonList(eObject));
editingDomain.getCommandStack().execute(createChildCommand);
Collection<?> result = createChildCommand.getResult();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2022 Obeo.
* Copyright (c) 2019, 2023 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
Expand Down Expand Up @@ -47,4 +47,9 @@ public String invalidNumber(String newValue) {
return this.messageSourceAccessor.getMessage("INVALID_NUMBER", new Object[] { newValue });
}

@Override
public String upperBoundaryReached(String newInstanceClass, String feature) {
return this.messageSourceAccessor.getMessage("UPPER_BOUNDARY_REACHED", new Object[] { newInstanceClass, feature });
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2022 Obeo.
* Copyright (c) 2019, 2023 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
Expand All @@ -25,12 +25,15 @@ public interface IEMFMessageService {

String invalidNumber(String newValue);

String upperBoundaryReached(String newInstanceClass, String feature);

/**
* Implementation which does nothing, used for mocks in unit tests.
*
* @author pcdavid
*/
class NoOp implements IEMFMessageService {

@Override
public String unexpectedError() {
return "";
Expand All @@ -45,5 +48,10 @@ public String invalidInput(String expectedInputTypeName, String receivedInputTyp
public String invalidNumber(String newValue) {
return "";
}

@Override
public String upperBoundaryReached(String newInstanceClass, String feature) {
return "";
}
}
}
Loading
Loading