Skip to content

Commit

Permalink
[2735] Contribute the first version of the Gantt representation
Browse files Browse the repository at this point in the history
This version contains:
Backend:
* GanttDescription as view model
* GanttDescription as POJO
* Gantt representation rendering
* Gantt representation subscription wiring
* Task meta-model with concept that will represented by Gantt
* An instance of the GanttRepresentation view model based on Task MM
that is usable as a stereotype (no GanttRepresentation is deployed at
server start)

Front:
* Gantt representation creation and display
* Toolbar with display options

Bug: #2735
Signed-off-by: Laurent Fasani <laurent.fasani@obeo.fr>
  • Loading branch information
lfasani committed Dec 8, 2023
1 parent b44c551 commit 04eb606
Show file tree
Hide file tree
Showing 21 changed files with 374 additions and 62 deletions.
Expand Up @@ -12,13 +12,19 @@
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.api.ISubscriptionManager;
import org.eclipse.sirius.components.collaborative.dto.RenameRepresentationInput;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttEventHandler;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttEventProcessor;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttInput;
import org.eclipse.sirius.components.collaborative.gantt.dto.input.RenameGanttInput;
import org.eclipse.sirius.components.collaborative.gantt.service.GanttCreationService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IInput;
Expand All @@ -36,8 +42,8 @@
import reactor.core.publisher.Sinks.One;

/**
* Reacts to the input that targets the gantt of a specific object and publishes updated versions of the
* {@link Gantt} to interested subscribers.
* Reacts to the input that targets the gantt of a specific object and publishes updated versions of the {@link Gantt}
* to interested subscribers.
*
* @author lfasani
*/
Expand All @@ -57,12 +63,16 @@ public class GanttEventProcessor implements IGanttEventProcessor {

private final GanttEventFlux ganttEventFlux;

public GanttEventProcessor(IEditingContext editingContext, Gantt ganttDiagram, ISubscriptionManager subscriptionManager, GanttCreationService ganttCreationService) {
private final List<IGanttEventHandler> ganttEventHandlers;

public GanttEventProcessor(IEditingContext editingContext, Gantt ganttDiagram, ISubscriptionManager subscriptionManager, GanttCreationService ganttCreationService,
List<IGanttEventHandler> ganttEventHandlers) {
this.logger.trace("Creating the gantt event processor {}", ganttDiagram.getId());

this.editingContext = Objects.requireNonNull(editingContext);
this.subscriptionManager = Objects.requireNonNull(subscriptionManager);
this.ganttCreationService = Objects.requireNonNull(ganttCreationService);
this.ganttEventHandlers = Objects.requireNonNull(ganttEventHandlers);

// We automatically refresh the representation before using it since things may have changed since the moment it
// has been saved in the database.
Expand All @@ -85,7 +95,21 @@ public ISubscriptionManager getSubscriptionManager() {

@Override
public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink, IRepresentationInput representationInput) {
// TODO
IRepresentationInput effectiveInput = representationInput;
if (representationInput instanceof RenameRepresentationInput renameRepresentationInput) {
effectiveInput = new RenameGanttInput(renameRepresentationInput.id(), renameRepresentationInput.editingContextId(), renameRepresentationInput.representationId(),
renameRepresentationInput.newLabel());
}
if (effectiveInput instanceof IGanttInput ganttInput) {
Optional<IGanttEventHandler> optionalGanttEventHandler = this.ganttEventHandlers.stream().filter(handler -> handler.canHandle(ganttInput)).findFirst();

if (optionalGanttEventHandler.isPresent()) {
IGanttEventHandler ganttEventHandler = optionalGanttEventHandler.get();
ganttEventHandler.handle(payloadSink, changeDescriptionSink, this.editingContext, this.currentGantt.get(), ganttInput);
} else {
this.logger.warn("No handler found for event: {}", ganttInput);
}
}
}

@Override
Expand All @@ -110,10 +134,7 @@ private boolean shouldRefresh(ChangeDescription changeDescription) {

@Override
public Flux<IPayload> getOutputEvents(IInput input) {
return Flux.merge(
this.ganttEventFlux.getFlux(input),
this.subscriptionManager.getFlux(input)
);
return Flux.merge(this.ganttEventFlux.getFlux(input), this.subscriptionManager.getFlux(input));
}

@Override
Expand Down
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt;

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

Expand All @@ -20,6 +21,7 @@
import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory;
import org.eclipse.sirius.components.collaborative.api.IRepresentationSearchService;
import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttEventHandler;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttEventProcessor;
import org.eclipse.sirius.components.collaborative.gantt.service.GanttCreationService;
import org.eclipse.sirius.components.core.api.IEditingContext;
Expand All @@ -40,11 +42,14 @@ public class GanttEventProcessorFactory implements IRepresentationEventProcessor

private final ISubscriptionManagerFactory subscriptionManagerFactory;

public GanttEventProcessorFactory(IRepresentationSearchService representationSearchService, GanttCreationService ganttCreationService,
ISubscriptionManagerFactory subscriptionManagerFactory) {
private final List<IGanttEventHandler> ganttEventHandlers;

public GanttEventProcessorFactory(IRepresentationSearchService representationSearchService, GanttCreationService ganttCreationService, ISubscriptionManagerFactory subscriptionManagerFactory,
List<IGanttEventHandler> ganttEventHandlers) {
this.representationSearchService = Objects.requireNonNull(representationSearchService);
this.ganttCreationService = Objects.requireNonNull(ganttCreationService);
this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory);
this.ganttEventHandlers = Objects.requireNonNull(ganttEventHandlers);
}

@Override
Expand All @@ -61,7 +66,7 @@ public <T extends IRepresentationEventProcessor> Optional<T> createRepresentatio
Gantt gantt = optionalGantt.get();

IRepresentationEventProcessor ganttEventProcessor = new GanttEventProcessor(editingContext, gantt,
this.subscriptionManagerFactory.create(), this.ganttCreationService);
this.subscriptionManagerFactory.create(), this.ganttCreationService, this.ganttEventHandlers);

return Optional.of(ganttEventProcessor)
.map(representationEventProcessorClass::cast);
Expand Down
@@ -0,0 +1,25 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt.dto.input;

import java.util.UUID;

import org.eclipse.sirius.components.collaborative.gantt.api.IGanttInput;

/**
* The input of the rename gantt mutation.
*
* @author lfasani
*/
public record RenameGanttInput(UUID id, String editingContextId, String representationId, String newLabel) implements IGanttInput {
}
Expand Up @@ -60,10 +60,10 @@ public class CreateGanttEventHandler implements IEditingContextEventHandler {
private final Counter counter;

public CreateGanttEventHandler(IRepresentationDescriptionSearchService representationDescriptionSearchService, IRepresentationPersistenceService representationPersistenceService,
IGanttCreationService diagramCreationService, IObjectService objectService, ICollaborativeMessageService messageService, MeterRegistry meterRegistry) {
IGanttCreationService ganttCreationService, IObjectService objectService, ICollaborativeMessageService messageService, MeterRegistry meterRegistry) {
this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService);
this.representationPersistenceService = Objects.requireNonNull(representationPersistenceService);
this.ganttCreationService = Objects.requireNonNull(diagramCreationService);
this.ganttCreationService = Objects.requireNonNull(ganttCreationService);
this.objectService = Objects.requireNonNull(objectService);
this.messageService = Objects.requireNonNull(messageService);

Expand Down Expand Up @@ -97,10 +97,10 @@ public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDesc
Optional<Object> optionalObject = this.objectService.getObject(editingContext, createRepresentationInput.objectId());

if (optionalDiagramDescription.isPresent() && optionalObject.isPresent()) {
GanttDescription diagramDescription = optionalDiagramDescription.get();
GanttDescription ganttDescription = optionalDiagramDescription.get();
Object object = optionalObject.get();

Gantt ganttDiagram = this.ganttCreationService.create(createRepresentationInput.representationName(), object, diagramDescription, editingContext);
Gantt ganttDiagram = this.ganttCreationService.create(createRepresentationInput.representationName(), object, ganttDescription, editingContext);

this.representationPersistenceService.save(editingContext, ganttDiagram);

Expand Down
@@ -0,0 +1,98 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt.handlers;

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

import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.api.IRepresentationPersistenceService;
import org.eclipse.sirius.components.collaborative.api.IRepresentationSearchService;
import org.eclipse.sirius.components.collaborative.api.Monitoring;
import org.eclipse.sirius.components.collaborative.dto.RenameRepresentationSuccessPayload;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttEventHandler;
import org.eclipse.sirius.components.collaborative.gantt.api.IGanttInput;
import org.eclipse.sirius.components.collaborative.gantt.dto.input.RenameGanttInput;
import org.eclipse.sirius.components.collaborative.gantt.message.ICollaborativeGanttMessageService;
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.gantt.Gantt;
import org.springframework.stereotype.Service;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import reactor.core.publisher.Sinks.Many;
import reactor.core.publisher.Sinks.One;

/**
* Handler used to rename a gantt.
*
* @author lfasani
*/
@Service
public class RenameGanttEventHandler implements IGanttEventHandler {

private final IRepresentationSearchService representationSearchService;

private final IRepresentationPersistenceService representationPersistenceService;

private final ICollaborativeGanttMessageService messageService;

private final Counter counter;

public RenameGanttEventHandler(IRepresentationSearchService representationSearchService, IRepresentationPersistenceService representationPersistenceService,
ICollaborativeGanttMessageService messageService, MeterRegistry meterRegistry) {
this.representationSearchService = Objects.requireNonNull(representationSearchService);
this.representationPersistenceService = Objects.requireNonNull(representationPersistenceService);
this.messageService = Objects.requireNonNull(messageService);

this.counter = Counter.builder(Monitoring.EVENT_HANDLER)
.tag(Monitoring.NAME, this.getClass().getSimpleName())
.register(meterRegistry);
}

@Override
public boolean canHandle(IGanttInput ganttInput) {
return ganttInput instanceof RenameGanttInput;
}

@Override
public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, Gantt gantt, IGanttInput ganttInput) {
this.counter.increment();

String message = this.messageService.invalidInput(ganttInput.getClass().getSimpleName(), RenameGanttInput.class.getSimpleName());
IPayload payload = new ErrorPayload(ganttInput.id(), message);
ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, ganttInput.representationId(), ganttInput);

if (ganttInput instanceof RenameGanttInput) {
RenameGanttInput renameRepresentationInput = (RenameGanttInput) ganttInput;
String representationId = renameRepresentationInput.representationId();
String newLabel = renameRepresentationInput.newLabel();
Optional<Gantt> optionalDiagram = this.representationSearchService.findById(editingContext, representationId, Gantt.class);
if (optionalDiagram.isPresent()) {
Gantt currentGantt = optionalDiagram.get();

Gantt renamedGantt = Gantt.newGantt(currentGantt).label(newLabel).build();
this.representationPersistenceService.save(editingContext, renamedGantt);

payload = new RenameRepresentationSuccessPayload(ganttInput.id(), renamedGantt);
changeDescription = new ChangeDescription(ChangeKind.REPRESENTATION_RENAMING, renameRepresentationInput.representationId(), ganttInput);
}
}

payloadSink.tryEmitValue(payload);
changeDescriptionSink.tryEmitNext(changeDescription);
}
}
@@ -0,0 +1,38 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt.message;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Service;

/**
* Implementation of the collaborative gantt message service.
*
* @author lfasani
*/
@Service
public class CollaborativeGanttMessageService implements ICollaborativeGanttMessageService {
private final MessageSourceAccessor messageSourceAccessor;

public CollaborativeGanttMessageService(@Qualifier("collaborativeGanttMessageSourceAccessor") MessageSourceAccessor messageSourceAccessor) {
this.messageSourceAccessor = Objects.requireNonNull(messageSourceAccessor);
}

@Override
public String invalidInput(String expectedInputTypeName, String receivedInputTypeName) {
return this.messageSourceAccessor.getMessage(MessageConstants.INVALID_INPUT, new Object[] { expectedInputTypeName, receivedInputTypeName });
}
}
@@ -0,0 +1,35 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt.message;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ResourceBundleMessageSource;

/**
* Configuration used to retrieve the message source accessor for the project.
*
* @author lfasani
*/
@Configuration
public class CollaborativeGanttMessageServiceConfiguration {
private static final String PATH = "messages/sirius-web-collaborative-gantt";

@Bean
public MessageSourceAccessor collaborativeGanttMessageSourceAccessor() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.addBasenames(PATH);
return new MessageSourceAccessor(messageSource);
}
}
@@ -0,0 +1,36 @@
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.sirius.components.collaborative.gantt.message;

/**
* Interface of the collaborative gantt message service.
*
* @author lfasani
*/
public interface ICollaborativeGanttMessageService {

String invalidInput(String expectedInputTypeName, String receivedInputTypeName);

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

@Override
public String invalidInput(String expectedInputTypeName, String receivedInputTypeName) {
return "";
}
}
}

0 comments on commit 04eb606

Please sign in to comment.