diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index a37c43ec24..4bd2bc9a91 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -100,6 +100,9 @@ Specifiers are also encouraged to implement their own `IRestDataVersionPayloadSe
The backend part (the ability to define an _Elements to Select Expression_ on diagram tools) was added in Sirius Web 2024.11.0 but the frontend did not apply the requested selection.
This is now fixed
- https://github.com/eclipse-sirius/sirius-web/issues/4297[#4297] Improve the size of the Docker image by optimizing the use of caching
+- https://github.com/eclipse-sirius/sirius-web/issues/4291[#4291] [core] Implement REST API documentation with openAPI v3 and swagger
+* Documentation is availlable in the url : /v3/api-docs
+* Swagger UI is availlable in the url : /swagger-ui/index.html#/
=== Improvements
diff --git a/packages/sirius-web/backend/sirius-web-application/pom.xml b/packages/sirius-web/backend/sirius-web-application/pom.xml
index 2eecfb2d94..5266bce85f 100644
--- a/packages/sirius-web/backend/sirius-web-application/pom.xml
+++ b/packages/sirius-web/backend/sirius-web-application/pom.xml
@@ -219,6 +219,11 @@
org.eclipse.emf.ecore.change
2.17.0
+
+ io.swagger.core.v3
+ swagger-annotations-jakarta
+ 2.2.21
+
org.eclipse.sirius
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/object/controllers/ObjectRestController.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/object/controllers/ObjectRestController.java
index d3eabe99e8..dfe13fd295 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/object/controllers/ObjectRestController.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/object/controllers/ObjectRestController.java
@@ -35,6 +35,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import io.swagger.v3.oas.annotations.Operation;
+
/**
* REST Controller for the Object Endpoint.
*
@@ -52,6 +54,7 @@ public ObjectRestController(IEditingContextDispatcher editingContextDispatcher)
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
}
+ @Operation(description = "Get all the elements in a given project at the given commit.")
@GetMapping(path = "/elements")
public ResponseEntity> getElements(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetElementsRestInput(UUID.randomUUID()))
@@ -62,6 +65,7 @@ public ResponseEntity> getElements(@PathVariable UUID projectId, @P
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Get element with the given id (elementId) in the given project at the given commit.")
@GetMapping(path = "/elements/{elementId}")
public ResponseEntity getElementById(@PathVariable UUID projectId, @PathVariable UUID commitId, @PathVariable UUID elementId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetElementByIdRestInput(UUID.randomUUID(), elementId.toString()))
@@ -72,6 +76,7 @@ public ResponseEntity getElementById(@PathVariable UUID projectId, @Path
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Get relationships that are incoming, outgoing, or both relative to the given related element.")
@GetMapping(path = "/elements/{relatedElementId}/relationships")
public ResponseEntity> getRelationshipsByRelatedElement(@PathVariable UUID projectId, @PathVariable UUID commitId, @PathVariable UUID relatedElementId, Optional direction) {
Direction directionParam = direction.orElse(Direction.BOTH);
@@ -84,6 +89,7 @@ public ResponseEntity> getRelationshipsByRelatedElement(@PathVariab
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Get all the root elements in the given project at the given commit.")
@GetMapping(path = "/roots")
public ResponseEntity> getRootElements(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetRootElementsRestInput(UUID.randomUUID()))
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ProjectRestController.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ProjectRestController.java
index e7b886685a..56ee4b52f6 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ProjectRestController.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/controllers/ProjectRestController.java
@@ -42,6 +42,8 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import io.swagger.v3.oas.annotations.Operation;
+
/**
* REST Controller for the Project Endpoint.
*
@@ -59,6 +61,7 @@ public ProjectRestController(IProjectApplicationService projectApplicationServic
this.projectApplicationService = Objects.requireNonNull(projectApplicationService);
}
+ @Operation(description = "Get all projects.")
@GetMapping
public ResponseEntity> getProjects() {
var restProjects = this.projectApplicationService.findAll(PageRequest.of(0, 20))
@@ -68,6 +71,7 @@ public ResponseEntity> getProjects() {
return new ResponseEntity<>(restProjects, HttpStatus.OK);
}
+ @Operation(description = "Get project with the given id (projectId).")
@GetMapping(path = "/{projectId}")
public ResponseEntity getProjectById(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
@@ -80,6 +84,7 @@ public ResponseEntity getProjectById(@PathVariable UUID projectId)
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Create a new project with the given name and description (optional).")
@PostMapping
public ResponseEntity createProject(@RequestParam String name, @RequestParam Optional description) {
var createProjectInput = new CreateProjectInput(UUID.randomUUID(), name, List.of());
@@ -94,6 +99,7 @@ public ResponseEntity createProject(@RequestParam String name, @Req
return null;
}
+ @Operation(description = "Update the project with the given id (projectId).")
@PutMapping(path = "/{projectId}")
public ResponseEntity updateProject(@PathVariable UUID projectId, @RequestParam Optional name, @RequestParam Optional description, @RequestParam Optional branch) {
if (name.isPresent()) {
@@ -108,6 +114,7 @@ public ResponseEntity updateProject(@PathVariable UUID projectId, @
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Delete the project with the given id (projectId).")
@DeleteMapping(path = "/{projectId}")
public ResponseEntity deleteProject(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/controllers/CommitRestController.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/controllers/CommitRestController.java
index 6dd327dd33..4937db711a 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/controllers/CommitRestController.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/controllers/CommitRestController.java
@@ -41,6 +41,8 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import io.swagger.v3.oas.annotations.Operation;
+
/**
* REST Controller for the Project Data Versioning - Commits Endpoints.
*
@@ -58,6 +60,7 @@ public CommitRestController(IEditingContextDispatcher editingContextDispatcher)
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
}
+ @Operation(description = "Get all the commits in the given project.")
@GetMapping
public ResponseEntity> getCommits(@PathVariable UUID projectId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitsRestInput(UUID.randomUUID())).block(Duration.ofSeconds(TIMEOUT));
@@ -67,6 +70,38 @@ public ResponseEntity> getCommits(@PathVariable UUID projectId)
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = """
+ Create a new commit with the given change (collection
+ of DataVersion records) in the given branch of the
+ project. If the branch is not specified, the default branch
+ of the project is used. Commit.change should include
+ the following for each Data object that needs to be
+ created, updated, or deleted in the new commit. (1)
+ Creating Data - Commit.change should include a
+ DataVersion record with DataVersion.payload
+ populated with the Data being created.
+ DataVersion.identity is not provided, thereby indicating
+ that a new DataIdentity needs to be created in the new
+ commit. (2) Updating Data - Commit.change should
+ include a DataVersion record with DataVersion.payload
+ populated with the updated Data. DataVersion.identity
+ should be populated with the DataIdentity for which a
+ new DataVersion record will be created in the new
+ commit. (3) Deleting Data - Commit.change should
+ include a DataVersion record with DataVersion.payload
+ not provided, thereby indicating deletion of DataIdentity
+ in the new commit. DataVersion.identity should be
+ populated with the DataIdentity that will be deleted in
+ the new commit. When a DataIdentity is deleted in a
+ commit, all its versions (DataVersion) are also deleted,
+ and any references from other DataIdentity are also
+ removed to maintain data integrity. In addition, for
+ Element Data (KerML), deletion of an Element must
+ also result in deletion of incoming Relationships. When
+ Element Data (KerML) is created or updated, derived
+ properties must be computed or verified if the API
+ provider claims Derived Property Conformance.
+ """)
@PostMapping
public ResponseEntity createCommit(@PathVariable UUID projectId, @RequestParam Optional branchId) {
var payload = this.editingContextDispatcher.dispatchMutation(projectId.toString(), new CreateCommitRestInput(UUID.randomUUID(), branchId)).block(Duration.ofSeconds(TIMEOUT));
@@ -77,6 +112,7 @@ public ResponseEntity createCommit(@PathVariable UUID projectId, @Re
return null;
}
+ @Operation(description = "Get the commit with the given id (commitId) in the given project.")
@GetMapping(path = "/{commitId}")
public ResponseEntity getCommitById(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitByIdRestInput(UUID.randomUUID(), commitId)).block(Duration.ofSeconds(TIMEOUT));
@@ -86,6 +122,7 @@ public ResponseEntity getCommitById(@PathVariable UUID projectId, @P
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Get the change in the given commit of the given project.")
@GetMapping(path = "/{commitId}/changes")
public ResponseEntity> getCommitChange(@PathVariable UUID projectId, @PathVariable UUID commitId, @RequestParam Optional> changeTypes) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitChangeRestInput(UUID.randomUUID(), commitId)).block(Duration.ofSeconds(TIMEOUT));
@@ -95,6 +132,7 @@ public ResponseEntity> getCommitChange(@PathVariable UUID
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
+ @Operation(description = "Get the change with the given id (changeId) in the given commit of the given project. The changeId is the id of the DataVersion that changed in the commit.")
@GetMapping(path = "/{commitId}/changes/{changeId}")
public ResponseEntity 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));
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestBranch.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestBranch.java
index 75c73ee1cc..3a9cb3c7d6 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestBranch.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestBranch.java
@@ -14,6 +14,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.UUID;
@@ -25,13 +27,28 @@
*
* @author arichard
*/
+@Schema(name = "Branch", description = "Branch is an indirect subclass of Record (via CommitReference) that represents an independent line of development in a Project. A Project can have 1 or more Branches. When a Project is created, a default Branch is also created. The default Branch of a Project can be changed, and a Project can have only 1 default Branch.")
public record RestBranch(
- @JsonProperty("@id") UUID id,
- @JsonProperty("@type") String type,
+ @Schema(required = true, description = "The UUID assigned to the record")
+ @JsonProperty("@id")
+ UUID id,
+
+ @JsonProperty("@type")
+ String type,
+
+ @Schema(required = true, description = "The timestamp at which the CommitReference was created")
OffsetDateTime created,
+
+ @Schema(description = "The Commit to which the Branch is currently pointing. It represents the latest state of the Project on the given Branch.")
Identified head,
+
+ @Schema(required = true, description = "The name of the Branch")
String name,
+
+ @Schema(required = true, description = "The Project that owns the given CommitReference")
Identified owningProject,
+
+ @Schema(required = true, description = "The commit referenced by the Branch")
Identified referencedCommit) {
public RestBranch {
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestCommit.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestCommit.java
index 93238e4702..3476f19e67 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestCommit.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestCommit.java
@@ -14,6 +14,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Objects;
@@ -26,6 +28,7 @@
*
* @author arichard
*/
+@Schema(name = "Commit")
public record RestCommit(
@JsonProperty("@id") UUID id,
@JsonProperty("@type") String type,
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataIdentity.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataIdentity.java
index 78b946f1f1..eb5a0d3407 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataIdentity.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataIdentity.java
@@ -14,6 +14,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.util.Objects;
import java.util.UUID;
@@ -22,8 +24,13 @@
*
* @author arichard
*/
+@Schema(name = "DataIdentity", description = "DataIdentity is a subclass of Record that represents a unique, version-independent representation of Data through its lifecycle. A DataIdentity is associated with 1 or more DataVersion records that represent different versions of the same Data.")
public record RestDataIdentity(
- @JsonProperty("@id") UUID id,
+
+ @Schema(required = true, description = "The UUID assigned to the record")
+ @JsonProperty("@id")
+ UUID id,
+
@JsonProperty("@type") String type) {
public RestDataIdentity {
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataVersion.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataVersion.java
index d101d3c941..8ea8430c82 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataVersion.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/data/versioning/dto/RestDataVersion.java
@@ -15,6 +15,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.util.Objects;
import java.util.UUID;
@@ -25,10 +27,18 @@
*
* @author arichard
*/
+@Schema(name = "DataVersion", description = "DataVersion is a subclass of Record that represents Data at a specific version in its lifecycle. A DataVersion record is associated with only one DataIdentity record. DataVersion serves as a wrapper for Data (payload) in the context of a Commit in a Project.")
public record RestDataVersion(
- @JsonProperty("@id") UUID id,
+
+ @Schema(required = true, description = "The UUID assigned to the record")
+ @JsonProperty("@id")
+ UUID id,
+
@JsonProperty("@type") String type,
+
RestDataIdentity identity,
+
+ @Schema(description = "The Payload assigned to the record")
@JsonSerialize(using = RestDataVersionPayloadSerializer.class)
Object payload) {
diff --git a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/dto/RestProject.java b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/dto/RestProject.java
index b2938cc70b..6f8928a0cf 100644
--- a/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/dto/RestProject.java
+++ b/packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/project/dto/RestProject.java
@@ -14,6 +14,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.time.OffsetDateTime;
import java.util.Objects;
import java.util.UUID;
@@ -25,13 +27,27 @@
*
* @author arichard
*/
+
+@Schema(name = "Project", description = "Project is a subclass of Record that represents a container for other Records and an entry point for version management and data navigation.")
public record RestProject(
- @JsonProperty("@id") UUID id,
- @JsonProperty("@type") String type,
+ @Schema(required = true, description = "The UUID assigned to the record")
+ @JsonProperty("@id")
+ UUID id,
+
+ @JsonProperty("@type")
+ String type,
+
OffsetDateTime created,
+
+ @Schema(required = true, description = "The default branch in the Project and a subset of branches")
Identified defaultBranch,
+
+ @Schema(description = "The statement that provides details about the record")
String description,
+
+ @Schema(required = true, description = "The name of the Project")
String name) {
+
public RestProject {
Objects.requireNonNull(id);
diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/pom.xml b/packages/sirius-web/backend/sirius-web-infrastructure/pom.xml
index 95952de424..050e9d0e2f 100644
--- a/packages/sirius-web/backend/sirius-web-infrastructure/pom.xml
+++ b/packages/sirius-web/backend/sirius-web-infrastructure/pom.xml
@@ -54,6 +54,11 @@
sirius-web-application
2024.11.7
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.5.0
+
org.eclipse.sirius
diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/configuration/openapi/OpenApiConfiguration.java b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/configuration/openapi/OpenApiConfiguration.java
new file mode 100644
index 0000000000..91918987a7
--- /dev/null
+++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/configuration/openapi/OpenApiConfiguration.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.infrastructure.configuration.openapi;
+
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configuration of the openapi generator.
+ *
+ * @author gescande
+ */
+@Configuration
+public class OpenApiConfiguration {
+
+ @Bean
+ public GroupedOpenApi selectedApiGroup() {
+ return GroupedOpenApi.builder()
+ .group("rest-apis")
+ .pathsToMatch("/api/rest/**")
+ .build();
+ }
+}
\ No newline at end of file