Skip to content

Commit

Permalink
[4193] Implement Project Data Versioning REST APIs
Browse files Browse the repository at this point in the history
Bug: #4193
Signed-off-by: Axel RICHARD <axel.richard@obeo.fr>
  • Loading branch information
AxelRICHARD committed Nov 20, 2024
1 parent 1d893b6 commit bcfad1b
Show file tree
Hide file tree
Showing 38 changed files with 1,893 additions and 133 deletions.
26 changes: 24 additions & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
=== Breaking changes

- https://github.com/eclipse-sirius/sirius-web/issues/4110[#4110] [view] CheckboxDescriptionStyle.labelPlacement is replaced by Grid layout properties
- https://github.com/eclipse-sirius/sirius-web/issues/4193[#4193] [core] Implement Project Data Versioning REST APIs.
The following classes/interfaces have been removed, they are not used anymore:
* RestQuery
* IRestRecord
* IRestCommitReference
The following records parameters have been modified:
* RestBranch:
** from RestBranch(UUID id, String resourceIdentifier, List<String> alias, String humanIdentifier, String description, OffsetDateTime created)
** to RestBranch(UUID id, String type, OffsetDateTime created, Identified head, Identified owningProject, Identified referencedCommit)
* RestProject
** from RestProject(UUID id, String resourceIdentifier, List<String> alias, String humanIdentifier, String description, List<RestQuery> queries)
** to RestProject(UUID id, String type, OffsetDateTime created, Identified defaultBranch, String description, String name)

=== Dependency update

Expand All @@ -27,16 +39,26 @@

- https://github.com/eclipse-sirius/sirius-web/issues/4195[#4195] [sirius-web] Prevent objects disappearing when dropped on themselves (or a descendant) in the _Explorer_.


=== New Features

- https://github.com/eclipse-sirius/sirius-web/issues/4110[#4110] [form] Add layout capabilities for form widgets
- https://github.com/eclipse-sirius/sirius-web/issues/4193[#4193] [core] Implement Project Data Versioning REST APIs.
The new endpoints are:
** getCommits (`GET /api/rest/projects/{projectId}/commits`): Get all commits in the given project. There is only one commit per project in Sirius Web for now, and its Id is the same as the project Id. It represents the current state of the project, without taking care of data created/updated/deleted since the creation of the project.
** createCommit (`POST /api/rest/projects/{projectId}/commits`): Create a commit in the given project. There is only one commit per project in Sirius Web for now, so the default implementation of this method does nothing.
** getCommitById (`GET /api/rest/projects/{projectId}/commits/{commitId}`): Get the commit represented by its Id for the given project. There is only one commit per project in Sirius Web for now, and its Id is the same as the project Id. It represents the current state of the project, without taking care of data created/updated/deleted since the creation of the project.
** getCommitChange (`GET /api/rest/projects/{projectId}/commits/{commitId}/changes`): Get all changes of the commit represented by its Id for the given project. There is only one commit per project in Sirius Web for now, and its Id is the same as the project Id. The default implementation retrieves all elements containing in the project, without taking care of data created/updated/deleted since the creation of the project.
** getCommitChangeById (`GET /api/rest/projects/{projectId}/commits/{commitId}/changes/{changeId}`): Get the changes associated to the given change Id for the commit represented by its Id for the given project. There is only one commit per project in Sirius Web for now, and its Id is the same as the project Id. The default implementation retrieves all changes containing in the project, without taking care of data created/updated/deleted since the creation of the project. The change Id parameter should be randomly generated but constant for an unlimited period. We decided to generate this Id from it's commit Id and element Id, to be able to compute it.
A new `IProjectDataVersioningRestServiceDelegate` interface is available, allowing to customize the default implementation of services related to Project Data Versioning related REST APIs.
Specifiers can implement this new interface with a spring `Service`.
A new `IRestDataVersionPayloadSerializerService` interface is available, allowing to customize the default implementation of the JSON serialization of the payload object of `RestDataVersion`.
Specifiers are also encouraged to implement their own `IRestDataVersionPayloadSerializerService` for their domains, as the default one may not return expected results.

=== Improvements





== v2024.11.0

=== Shapes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,17 @@
*******************************************************************************/
package org.eclipse.sirius.web.application.dto;

import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;

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

/**
* Interface for the REST Record DTO.
*
* Simple record allowing to serialize an object with only its id.
* @author arichard
*/
public interface IRestRecord {

UUID id();

String resourceIdentifier();

List<String> alias();

String humanIdentifier();

String decription();
public record Identified(@JsonProperty("@id") UUID id) {
public Identified {
Objects.requireNonNull(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
*******************************************************************************/
package org.eclipse.sirius.web.application.project.controllers;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.web.application.dto.Identified;
import org.eclipse.sirius.web.application.project.data.versioning.dto.RestBranch;
import org.eclipse.sirius.web.application.project.dto.CreateProjectInput;
import org.eclipse.sirius.web.application.project.dto.CreateProjectSuccessPayload;
Expand Down Expand Up @@ -47,6 +51,8 @@
@RequestMapping("/api/rest/projects")
public class ProjectRestController {

private static final OffsetDateTime DEFAULT_CREATED = Instant.EPOCH.atOffset(ZoneOffset.UTC);

private final IProjectApplicationService projectApplicationService;

public ProjectRestController(IProjectApplicationService projectApplicationService) {
Expand All @@ -56,7 +62,7 @@ public ProjectRestController(IProjectApplicationService projectApplicationServic
@GetMapping
public ResponseEntity<List<RestProject>> getProjects() {
var restProjects = this.projectApplicationService.findAll(PageRequest.of(0, 20))
.map(project -> new RestProject(project.id(), null, List.of(), null, null, project.name(), List.of()))
.map(project -> new RestProject(project.id(), DEFAULT_CREATED, new Identified(project.id()), null, project.name()))
.toList();

return new ResponseEntity<>(restProjects, HttpStatus.OK);
Expand All @@ -65,7 +71,7 @@ public ResponseEntity<List<RestProject>> getProjects() {
@GetMapping(path = "/{projectId}")
public ResponseEntity<RestProject> getProjectById(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
.map(project -> new RestProject(project.id(), null, List.of(), null, null, project.name(), List.of()));
.map(project -> new RestProject(project.id(), DEFAULT_CREATED, new Identified(project.id()), null, project.name()));

if (restProject.isPresent()) {
return new ResponseEntity<>(restProject.get(), HttpStatus.OK);
Expand All @@ -81,7 +87,7 @@ public ResponseEntity<RestProject> createProject(@RequestParam String name, @Req

if (newProjectPayload instanceof CreateProjectSuccessPayload createProjectSuccessPayload) {
var projectDTO = createProjectSuccessPayload.project();
var restProject = new RestProject(projectDTO.id(), null, List.of(), null, null, projectDTO.name(), List.of());
var restProject = new RestProject(projectDTO.id(), DEFAULT_CREATED, new Identified(projectDTO.id()), null, projectDTO.name());
return new ResponseEntity<>(restProject, HttpStatus.CREATED);
}
// The specification does not handle other HttpStatus than HttpStatus.CREATED for this endpoint
Expand All @@ -94,7 +100,7 @@ public ResponseEntity<RestProject> updateProject(@PathVariable UUID projectId, @
var renameProjectInput = new RenameProjectInput(UUID.randomUUID(), projectId, name.get());
var renamedProjectPayload = this.projectApplicationService.renameProject(renameProjectInput);
if (renamedProjectPayload instanceof RenameProjectSuccessPayload) {
var restProject = new RestProject(projectId, null, List.of(), null, null, name.get(), List.of());
var restProject = new RestProject(projectId, DEFAULT_CREATED, new Identified(projectId), null, name.get());
return new ResponseEntity<>(restProject, HttpStatus.OK);
}
}
Expand All @@ -105,7 +111,7 @@ public ResponseEntity<RestProject> updateProject(@PathVariable UUID projectId, @
@DeleteMapping(path = "/{projectId}")
public ResponseEntity<RestProject> deleteProject(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
.map(project -> new RestProject(project.id(), null, List.of(), null, null, project.name(), List.of()))
.map(project -> new RestProject(project.id(), DEFAULT_CREATED, new Identified(project.id()), null, project.name()))
.orElse(null);

var deleteProjectInput = new DeleteProjectInput(UUID.randomUUID(), projectId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*******************************************************************************
* 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.application.project.data.versioning.controllers;

import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher;
import org.eclipse.sirius.web.application.project.data.versioning.dto.ChangeType;
import org.eclipse.sirius.web.application.project.data.versioning.dto.CreateCommitRestInput;
import org.eclipse.sirius.web.application.project.data.versioning.dto.CreateCommitRestSuccessPayload;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitByIdRestInput;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitByIdRestSuccessPayload;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitChangeByIdRestInput;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitChangeByIdRestSuccessPayload;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitChangeRestInput;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitChangeRestSuccessPayload;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitsRestInput;
import org.eclipse.sirius.web.application.project.data.versioning.dto.GetCommitsRestSuccessPayload;
import org.eclipse.sirius.web.application.project.data.versioning.dto.RestCommit;
import org.eclipse.sirius.web.application.project.data.versioning.dto.RestDataVersion;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* REST Controller for the Project Data Versioning - Commits Endpoints.
*
* @author arichard
*/
@RestController
@RequestMapping("/api/rest/projects/{projectId}/commits")
public class CommitRestController {

private static final int TIMEOUT = 20;

private final IEditingContextDispatcher editingContextDispatcher;

public CommitRestController(IEditingContextDispatcher editingContextDispatcher) {
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
}

@GetMapping
public ResponseEntity<List<RestCommit>> getCommits(@PathVariable UUID projectId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitsRestInput(UUID.randomUUID())).block(Duration.ofSeconds(TIMEOUT));
if (payload instanceof GetCommitsRestSuccessPayload successPayload) {
return new ResponseEntity<>(successPayload.commits(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}

@PostMapping
public ResponseEntity<RestCommit> createCommit(@PathVariable UUID projectId, @RequestParam Optional<UUID> branchId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new CreateCommitRestInput(UUID.randomUUID(), branchId)).block(Duration.ofSeconds(TIMEOUT));
if (payload instanceof CreateCommitRestSuccessPayload successPayload) {
return new ResponseEntity<>(successPayload.commit(), HttpStatus.OK);
}
// The specification does not handle other HttpStatus than HttpStatus.CREATED for this endpoint
return null;
}

@GetMapping(path = "/{commitId}")
public ResponseEntity<RestCommit> getCommitById(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitByIdRestInput(UUID.randomUUID(), commitId)).block(Duration.ofSeconds(TIMEOUT));
if (payload instanceof GetCommitByIdRestSuccessPayload successPayload) {
return new ResponseEntity<>(successPayload.commit(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}

@GetMapping(path = "/{commitId}/changes")
public ResponseEntity<List<RestDataVersion>> getCommitChange(@PathVariable UUID projectId, @PathVariable UUID commitId, @RequestParam Optional<List<ChangeType>> changeTypes) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitChangeRestInput(UUID.randomUUID(), commitId)).block(Duration.ofSeconds(TIMEOUT));
if (payload instanceof GetCommitChangeRestSuccessPayload successPayload) {
return new ResponseEntity<>(successPayload.commitChanges(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}

@GetMapping(path = "/{commitId}/changes/{changeId}")
public ResponseEntity<RestDataVersion> getCommitChangeById(@PathVariable UUID projectId, @PathVariable UUID commitId, @PathVariable UUID changeId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitChangeByIdRestInput(UUID.randomUUID(), commitId, changeId)).block(Duration.ofSeconds(TIMEOUT));
if (payload instanceof GetCommitChangeByIdRestSuccessPayload successPayload) {
return new ResponseEntity<>(successPayload.commitChange(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*******************************************************************************
* 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.application.project.data.versioning.dto;

/**
* Used to indicate the type of commit when calling "getCommitChange" or "diffCommits" REST APIs.
*
* @author arichard
*/
public enum ChangeType {
CREATED, UPDATED, DELETED
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,18 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.web.application.query.dto;
package org.eclipse.sirius.web.application.project.data.versioning.dto;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.eclipse.sirius.web.application.dto.IRestRecord;
import org.eclipse.sirius.components.core.api.IInput;

/**
* REST Query DTO.
* The input object of the CreateCommitRestEventHandler.
*
* @author arichard
*/
public record RestQuery(
UUID id,
String resourceIdentifier,
List<String> alias,
String humanIdentifier,
String decription) implements IRestRecord {
public record CreateCommitRestInput(UUID id, Optional<UUID> branchId) implements IInput {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* 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.application.project.data.versioning.dto;

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

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

/**
* The payload for the "createCommit" REST API on success.
*
* @author arichard
*/
public record CreateCommitRestSuccessPayload(UUID id, RestCommit commit) implements IPayload {
public CreateCommitRestSuccessPayload {
Objects.requireNonNull(id);
Objects.requireNonNull(commit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@
*******************************************************************************/
package org.eclipse.sirius.web.application.project.data.versioning.dto;

import java.time.OffsetDateTime;
import java.util.UUID;

import org.eclipse.sirius.web.application.dto.IRestRecord;
import org.eclipse.sirius.components.core.api.IInput;

/**
* Interface for the REST CommitReference DTO.
* The input object of the GetCommitByIdRestEventHandler.
*
* @author arichard
*/
public interface IRestCommitReference extends IRestRecord {
public record GetCommitByIdRestInput(UUID id, UUID commitId) implements IInput {

OffsetDateTime created();

String name();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* 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.application.project.data.versioning.dto;

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

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

/**
* The payload for the "getCommitById" REST API on success.
*
* @author arichard
*/
public record GetCommitByIdRestSuccessPayload(UUID id, RestCommit commit) implements IPayload {
public GetCommitByIdRestSuccessPayload {
Objects.requireNonNull(id);
Objects.requireNonNull(commit);
}
}
Loading

0 comments on commit bcfad1b

Please sign in to comment.