Skip to content

Commit

Permalink
[4177] Add table representation support
Browse files Browse the repository at this point in the history
Bug: #4177
Signed-off-by: Florian ROUËNÉ <[email protected]>
  • Loading branch information
frouene committed Nov 6, 2024
1 parent e0fc72f commit a0ff15b
Show file tree
Hide file tree
Showing 40 changed files with 1,975 additions and 159 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

=== New Features


- https://github.com/eclipse-sirius/sirius-web/issues/4177[#4177] [table] Add table representation support

=== Improvements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
* @author sbegaudeau
*/
public class FormElementFactory implements IElementFactory {

private final List<IWidgetDescriptor> widgetDescriptors;

private final TableElementFactory tableElementFactory = new TableElementFactory();
Expand Down Expand Up @@ -149,7 +150,7 @@ public Object instantiateElement(String type, IProps props, List<Object> childre
} else if (TableWidgetElementProps.TYPE.equals(type) && props instanceof TableWidgetElementProps) {
object = this.instantiateTableWidget((TableWidgetElementProps) props, children);
} else {
object = tableElementFactory.instantiateElement(type, props, children);
object = this.tableElementFactory.instantiateElement(type, props, children);
if (object == null) {
object = this.widgetDescriptors.stream()
.map(widgetDescriptor -> widgetDescriptor.instanciate(type, props, children))
Expand Down Expand Up @@ -488,9 +489,9 @@ private Button instantiateButton(ButtonElementProps props, List<Object> children
private SplitButton instantiateSplitButton(SplitButtonElementProps props, List<Object> children) {
List<Diagnostic> diagnostics = this.getDiagnosticsFromChildren(children);
List<Button> actions = children.stream()
.filter(Button.class::isInstance)
.map(Button.class::cast)
.toList();
.filter(Button.class::isInstance)
.map(Button.class::cast)
.toList();

SplitButton.Builder buttonBuilder = SplitButton.newSplitButton(props.getId())
.label(props.getLabel())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2024 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.representations.table;

import java.util.List;

import org.eclipse.sirius.components.collaborative.api.IRepresentationDescriptionsProvider;
import org.eclipse.sirius.components.collaborative.api.RepresentationDescriptionMetadata;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.representations.IRepresentationDescription;
import org.springframework.stereotype.Service;

/**
* Used to provide the RepresentationDescriptionMetadata for papaya packages table.
*
* @author Jerome Gout
*/
@Service
public class PackageTableRepresentationDescription implements IRepresentationDescriptionsProvider {

@Override
public boolean canHandle(IRepresentationDescription representationDescription) {
return representationDescription.getId().equals(PackageTableRepresentationDescriptionProvider.TABLE_DESCRIPTION_ID);
}

@Override
public List<RepresentationDescriptionMetadata> handle(IEditingContext editingContext, Object object, IRepresentationDescription representationDescription) {
return List.of(new RepresentationDescriptionMetadata(representationDescription.getId(), representationDescription.getLabel(), representationDescription.getLabel()));
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*******************************************************************************
* 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
* 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.tests.services.api;

import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput;

import reactor.core.publisher.Flux;

/**
* Used to create a table and subscribe to it.
*
* @author frouene
*/
public interface IGivenCreatedTableSubscription {

Flux<Object> createAndSubscribe(CreateRepresentationInput input);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* 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
* 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.tests.services.tables;

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

import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput;
import org.eclipse.sirius.components.collaborative.tables.TableEventInput;
import org.eclipse.sirius.components.tables.tests.graphql.TableEventSubscriptionRunner;
import org.eclipse.sirius.web.tests.services.api.IGivenCommittedTransaction;
import org.eclipse.sirius.web.tests.services.api.IGivenCreatedRepresentation;
import org.eclipse.sirius.web.tests.services.api.IGivenCreatedTableSubscription;
import org.springframework.stereotype.Service;
import org.springframework.test.context.transaction.TestTransaction;

import graphql.execution.DataFetcherResult;
import reactor.core.publisher.Flux;

/**
* Used to create a table and subscribe to it.
*
* @author frouene
*/
@Service
public class GivenCreatedTableSubscription implements IGivenCreatedTableSubscription {

private final IGivenCommittedTransaction givenCommittedTransaction;

private final IGivenCreatedRepresentation givenCreatedRepresentation;

private final TableEventSubscriptionRunner tableEventSubscriptionRunner;

public GivenCreatedTableSubscription(IGivenCommittedTransaction givenCommittedTransaction, IGivenCreatedRepresentation givenCreatedRepresentation,
TableEventSubscriptionRunner tableEventSubscriptionRunner) {
this.givenCommittedTransaction = Objects.requireNonNull(givenCommittedTransaction);
this.givenCreatedRepresentation = Objects.requireNonNull(givenCreatedRepresentation);
this.tableEventSubscriptionRunner = Objects.requireNonNull(tableEventSubscriptionRunner);
}

@Override
public Flux<Object> createAndSubscribe(CreateRepresentationInput input) {
this.givenCommittedTransaction.commit();

String representationId = this.givenCreatedRepresentation.createRepresentation(input);

var tableEventInput = new TableEventInput(UUID.randomUUID(), input.editingContextId(), representationId);
var flux = this.tableEventSubscriptionRunner.run(tableEventInput);

TestTransaction.flagForCommit();
TestTransaction.end();
TestTransaction.start();

return flux.filter(DataFetcherResult.class::isInstance)
.map(DataFetcherResult.class::cast)
.map(DataFetcherResult::getData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*******************************************************************************
* 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
* 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.controllers.tables;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;

import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput;
import org.eclipse.sirius.components.collaborative.tables.TableRefreshedEventPayload;
import org.eclipse.sirius.web.AbstractIntegrationTests;
import org.eclipse.sirius.web.data.PapayaIdentifiers;
import org.eclipse.sirius.web.tests.services.api.IGivenCreatedTableSubscription;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.transaction.annotation.Transactional;

import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
* Integration tests of the table representation with a papaya model.
*
* @author frouene
*/
@Transactional
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"sirius.web.test.enabled=studio"})
public class PapayaTableControllerIntegrationTests extends AbstractIntegrationTests {

private static final String MISSING_TABLE = "Missing table";

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenCreatedTableSubscription givenCreatedTableSubscription;

@BeforeEach
public void beforeEach() {
this.givenInitialServerState.initialize();
}

private Flux<Object> givenSubscriptionToTable() {
var input = new CreateRepresentationInput(
UUID.randomUUID(),
PapayaIdentifiers.PAPAYA_PROJECT.toString(),
"papaya_package_table_description",
PapayaIdentifiers.SIRIUS_WEB_DOMAIN_PACKAGE.toString(),
"Table"
);
return this.givenCreatedTableSubscription.createAndSubscribe(input);
}

@Test
@DisplayName("Given a table representation, when we subscribe to its event, then the representation data are received")
@Sql(scripts = {"/scripts/papaya.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 givenTableRepresentationWhenWeSubscribeToItsEventThenTheRepresentationDataAreReceived() {
var flux = this.givenSubscriptionToTable();

Consumer<Object> initialTableContentConsumer = payload -> Optional.of(payload)
.filter(TableRefreshedEventPayload.class::isInstance)
.map(TableRefreshedEventPayload.class::cast)
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(4);
assertThat(table.getLines()).hasSize(2);
}, () -> fail(MISSING_TABLE));

StepVerifier.create(flux)
.consumeNextWith(initialTableContentConsumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public final class PapayaIdentifiers {

public static final UUID SIRIUS_WEB_DOMAIN_OBJECT = UUID.fromString("fad0f4c9-e668-44f3-8deb-aef0edb6ddff");

public static final UUID SIRIUS_WEB_DOMAIN_PACKAGE = UUID.fromString("569d3f9b-2a43-4254-b609-511258251d96");


private PapayaIdentifiers() {
// Prevent instantiation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public FormWithTableEditingContextDescriptionProvider(ComposedAdapterFactory com
@Override
public List<IRepresentationDescription> getRepresentationDescriptions(IEditingContext editingContext) {

TableWidgetDescription tableWidgetDescription = getTableWidgetDescription();
TableWidgetDescription tableWidgetDescription = this.getTableWidgetDescription();

GroupDescription taskGroup = GroupDescription.newGroupDescription("iterationGroupId")
.idProvider(variableManager -> "iterationGroupId")
Expand All @@ -112,7 +112,7 @@ public List<IRepresentationDescription> getRepresentationDescriptions(IEditingCo
.canCreatePredicate(variableManager -> true)
.build();

FormDescription formDescription = FormDescription.newFormDescription(TASK_FORM_ID)
FormDescription formDescription = FormDescription.newFormDescription(TASK_FORM_ID)
.label("Iteration form description")
.idProvider(new GetOrCreateRandomIdProvider())
.labelProvider(variableManager -> "Iteration Form")
Expand Down Expand Up @@ -145,12 +145,13 @@ private TableWidgetDescription getTableWidgetDescription() {
lineDescriptions.add(lineDescription);

TableDescription tableDescription = TableDescription.newTableDescription("tasksTableId")
.label("tasksTableLabel")
.targetObjectIdProvider(this::getTargetObjectId)
.targetObjectKindProvider(this::getTargetObjectKind)
.labelProvider(labelProvider)
.lineDescriptions(lineDescriptions)
.columnDescriptions(getColumnDescriptions())
.cellDescription(getCellDescription())
.columnDescriptions(this.getColumnDescriptions())
.cellDescription(this.getCellDescription())
.build();

return TableWidgetDescription.newTableWidgetDescription("tasksTableWidgetId")
Expand All @@ -166,14 +167,14 @@ private TableWidgetDescription getTableWidgetDescription() {

private CellDescription getCellDescription() {
return CellDescription.newCellDescription("cells")
.targetObjectIdProvider(vm-> "")
.targetObjectKindProvider(vm-> "")
.targetObjectIdProvider(vm -> "")
.targetObjectKindProvider(vm -> "")
.cellTypeProvider(this.getCellTypeProvider())
.cellValueProvider(this.getCellValueProvider())
.cellOptionsIdProvider(this.getCellOptionsIdProvider())
.cellOptionsLabelProvider(this.getCellOptionsLabelProvider())
.cellOptionsProvider(this.getCellOptionsProvider())
.newCellValueHandler(getNewCellValueHandler())
.newCellValueHandler(this.getNewCellValueHandler())
.build();
}

Expand Down Expand Up @@ -207,15 +208,14 @@ private List<ColumnDescription> getColumnDescriptions() {
return List.of(columnDescription);
}

private BiFunction<VariableManager, Object, Object> getCellValueProvider() {
private BiFunction<VariableManager, Object, Object> getCellValueProvider() {
return (variableManager, columnTargetObject) -> {
Object value = "";
Optional<EObject> optionalEObject = variableManager.get(VariableManager.SELF, EObject.class);
if (optionalEObject.isPresent() && columnTargetObject instanceof EStructuralFeature eStructuralFeature) {
EObject eObject = optionalEObject.get();
Object objectValue = eObject.eGet(eStructuralFeature);
if (eStructuralFeature instanceof EReference) {
EReference eReference = (EReference) eStructuralFeature;
if (eStructuralFeature instanceof EReference eReference) {
if (eReference.isMany() && !eReference.isContainment() && objectValue instanceof EList<?>) {
value = ((EList<?>) objectValue).stream().map(this.objectService::getId).collect(Collectors.toList());
} else if (!eReference.isMany() && !eReference.isContainment()) {
Expand Down Expand Up @@ -269,7 +269,7 @@ private Function<VariableManager, String> getCellOptionsIdProvider() {
}

private Function<VariableManager, String> getCellOptionsLabelProvider() {
return variableManager -> {
return variableManager -> {
Object candidate = variableManager.getVariables().get(SelectCellComponent.CANDIDATE_VARIABLE);
if (candidate instanceof EEnumLiteral) {
return this.objectService.getLabel(candidate);
Expand All @@ -289,14 +289,12 @@ private BiFunction<VariableManager, Object, List<Object>> getCellOptionsProvider
options.addAll(((EEnum) eType).getELiterals());
} else {
Object adapter = this.composedAdapterFactory.adapt(eObject, IItemPropertySource.class);
if (adapter instanceof IItemPropertySource) {
IItemPropertySource itemPropertySource = (IItemPropertySource) adapter;
if (adapter instanceof IItemPropertySource itemPropertySource) {
IItemPropertyDescriptor descriptor = itemPropertySource.getPropertyDescriptor(eObject, eStructuralFeature);
if (descriptor != null) {
List<Object> choiceOfValues = descriptor.getChoiceOfValues(eObject).stream()
return descriptor.getChoiceOfValues(eObject).stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
return choiceOfValues;
}
}
}
Expand All @@ -320,8 +318,7 @@ private Map<EStructuralFeature, String> getColumnsStructuralFeaturesDisplayName(

private String getDisplayName(EObject eObject, EStructuralFeature eStructuralFeature) {
Adapter adapter = this.composedAdapterFactory.adapt(eObject, IItemPropertySource.class);
if (adapter instanceof IItemPropertySource) {
IItemPropertySource itemPropertySource = (IItemPropertySource) adapter;
if (adapter instanceof IItemPropertySource itemPropertySource) {
IItemPropertyDescriptor descriptor = itemPropertySource.getPropertyDescriptor(eObject, eStructuralFeature);
if (descriptor != null) {
String displayName = descriptor.getDisplayName(eStructuralFeature);
Expand Down
Loading

0 comments on commit a0ff15b

Please sign in to comment.