Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4291] Implement API documentation with openAPI v3 and swagger #4322

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions packages/sirius-web/backend/sirius-web-application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@
<artifactId>org.eclipse.emf.ecore.change</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.21</version>
</dependency>

<dependency>
<groupId>org.eclipse.sirius</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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<List<Object>> getElements(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetElementsRestInput(UUID.randomUUID()))
Expand All @@ -62,6 +65,7 @@ public ResponseEntity<List<Object>> 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<Object> getElementById(@PathVariable UUID projectId, @PathVariable UUID commitId, @PathVariable UUID elementId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetElementByIdRestInput(UUID.randomUUID(), elementId.toString()))
Expand All @@ -72,6 +76,7 @@ public ResponseEntity<Object> 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<List<Object>> getRelationshipsByRelatedElement(@PathVariable UUID projectId, @PathVariable UUID commitId, @PathVariable UUID relatedElementId, Optional<Direction> direction) {
Direction directionParam = direction.orElse(Direction.BOTH);
Expand All @@ -84,6 +89,7 @@ public ResponseEntity<List<Object>> 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<List<Object>> getRootElements(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetRootElementsRestInput(UUID.randomUUID()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -59,6 +61,7 @@ public ProjectRestController(IProjectApplicationService projectApplicationServic
this.projectApplicationService = Objects.requireNonNull(projectApplicationService);
}

@Operation(description = "Get all projects.")
@GetMapping
public ResponseEntity<List<RestProject>> getProjects() {
var restProjects = this.projectApplicationService.findAll(PageRequest.of(0, 20))
Expand All @@ -68,6 +71,7 @@ public ResponseEntity<List<RestProject>> getProjects() {
return new ResponseEntity<>(restProjects, HttpStatus.OK);
}

@Operation(description = "Get project with the given id (projectId).")
@GetMapping(path = "/{projectId}")
public ResponseEntity<RestProject> getProjectById(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
Expand All @@ -80,6 +84,7 @@ public ResponseEntity<RestProject> 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<RestProject> createProject(@RequestParam String name, @RequestParam Optional<String> description) {
var createProjectInput = new CreateProjectInput(UUID.randomUUID(), name, List.of());
Expand All @@ -94,6 +99,7 @@ public ResponseEntity<RestProject> createProject(@RequestParam String name, @Req
return null;
}

@Operation(description = "Update the project with the given id (projectId).")
@PutMapping(path = "/{projectId}")
public ResponseEntity<RestProject> updateProject(@PathVariable UUID projectId, @RequestParam Optional<String> name, @RequestParam Optional<String> description, @RequestParam Optional<RestBranch> branch) {
if (name.isPresent()) {
Expand All @@ -108,6 +114,7 @@ public ResponseEntity<RestProject> 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<RestProject> deleteProject(@PathVariable UUID projectId) {
var restProject = this.projectApplicationService.findById(projectId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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<List<RestCommit>> getCommits(@PathVariable UUID projectId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitsRestInput(UUID.randomUUID())).block(Duration.ofSeconds(TIMEOUT));
Expand All @@ -67,6 +70,38 @@ public ResponseEntity<List<RestCommit>> 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<RestCommit> createCommit(@PathVariable UUID projectId, @RequestParam Optional<UUID> branchId) {
var payload = this.editingContextDispatcher.dispatchMutation(projectId.toString(), new CreateCommitRestInput(UUID.randomUUID(), branchId)).block(Duration.ofSeconds(TIMEOUT));
Expand All @@ -77,6 +112,7 @@ public ResponseEntity<RestCommit> 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<RestCommit> getCommitById(@PathVariable UUID projectId, @PathVariable UUID commitId) {
var payload = this.editingContextDispatcher.dispatchQuery(projectId.toString(), new GetCommitByIdRestInput(UUID.randomUUID(), commitId)).block(Duration.ofSeconds(TIMEOUT));
Expand All @@ -86,6 +122,7 @@ public ResponseEntity<RestCommit> 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<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));
Expand All @@ -95,6 +132,7 @@ public ResponseEntity<List<RestDataVersion>> 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<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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +28,7 @@
*
* @author arichard
*/
@Schema(name = "Commit")
public record RestCommit(
@JsonProperty("@id") UUID id,
@JsonProperty("@type") String type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
<artifactId>sirius-web-application</artifactId>
<version>2024.11.7</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>

<dependency>
<groupId>org.eclipse.sirius</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading