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