From 6fb26de3230a78595eb07bf5884ae53e4a79d928 Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Fri, 14 Jun 2019 08:29:49 +0200
Subject: [PATCH] OO-4096: integrate Swagger for the REST API documentation

---
 pom.xml                                       | 15 +++++
 .../admin/restapi/RestapiAdminController.java |  4 ++
 .../olat/admin/restapi/_content/docLink.html  |  2 +-
 .../restapi/_i18n/LocalStrings_de.properties  |  3 +
 .../restapi/_i18n/LocalStrings_en.properties  |  3 +
 .../olat/restapi/_spring/restApiContext.xml   |  7 +++
 .../RepositoryEntriesWebService.java          | 59 +++++--------------
 7 files changed, 49 insertions(+), 44 deletions(-)

diff --git a/pom.xml b/pom.xml
index 773164874bf..1a69f130bae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -424,6 +424,16 @@
 					<artifactId>cxf-rt-rs-http-sci</artifactId>
 					<version>${apache.cxf}</version>
 				</dependency>
+				<dependency>
+					<groupId>org.apache.cxf</groupId>
+				    <artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
+				    <version>${apache.cxf}</version>
+				</dependency>
+				<dependency>
+				    <groupId>org.webjars</groupId>
+				    <artifactId>swagger-ui</artifactId>
+				    <version>3.22.2</version>
+				</dependency>
 				<dependency>
 				    <groupId>jakarta.xml.ws</groupId>
 				    <artifactId>jakarta.xml.ws-api</artifactId>
@@ -2483,6 +2493,11 @@
         	<groupId>com.fasterxml.jackson.core</groupId>
         	<artifactId>jackson-databind</artifactId>
             <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+        	<groupId>com.fasterxml.jackson.dataformat</groupId>
+        	<artifactId>jackson-dataformat-yaml</artifactId>
+            <version>${jackson.version}</version>
         </dependency>
 		<!-- end Apache CXF dependency and Active MQ Broker -->
 		
diff --git a/src/main/java/org/olat/admin/restapi/RestapiAdminController.java b/src/main/java/org/olat/admin/restapi/RestapiAdminController.java
index 071be9a4fe2..3825618bfb4 100644
--- a/src/main/java/org/olat/admin/restapi/RestapiAdminController.java
+++ b/src/main/java/org/olat/admin/restapi/RestapiAdminController.java
@@ -94,6 +94,10 @@ public class RestapiAdminController extends FormBasicController {
 			
 			String link = Settings.getServerContextPathURI() + RestSecurityHelper.SUB_CONTEXT + "/api/doc";
 			docLinkFlc.contextPut("docLink", link);
+			String openApiLink = Settings.getServerContextPathURI() + RestSecurityHelper.SUB_CONTEXT + "/openapi.json";
+			docLinkFlc.contextPut("openApiLink", openApiLink);
+			String swaggerUiLink = Settings.getServerContextPathURI() + RestSecurityHelper.SUB_CONTEXT + "/api-docs/?url=" + RestSecurityHelper.SUB_CONTEXT + "/openapi.json";
+			docLinkFlc.contextPut("swaggerUiLink", swaggerUiLink);
 			
 			FormLayoutContainer accessDataFlc = FormLayoutContainer.createDefaultFormLayout("flc_access_data", getTranslator());
 			layoutContainer.add(accessDataFlc);
diff --git a/src/main/java/org/olat/admin/restapi/_content/docLink.html b/src/main/java/org/olat/admin/restapi/_content/docLink.html
index 396df069dd7..1f4fa6035b2 100644
--- a/src/main/java/org/olat/admin/restapi/_content/docLink.html
+++ b/src/main/java/org/olat/admin/restapi/_content/docLink.html
@@ -1 +1 @@
-<p>$r.translate("rest.doc"): <a href="$docLink" target="_blanck">$docLink</a></p>
\ No newline at end of file
+<p>$r.translate("rest.doc"): <a href="$docLink" target="_blanck">$docLink <i class="o_icon o_icon_content_popup"> </i></a><p><h5><i class="o_icon o_icon_warn"> </i> $r.translate("rest.doc.openapi.experimental")</h5><p>$r.translate("rest.doc.openapi"): <a href="$openApiLink" target="_blanck">$openApiLink <i class="o_icon o_icon_code"> </i></a><br>$r.translate("rest.doc.swagger.ui"): <a href="$swaggerUiLink" target="_blanck">$swaggerUiLink <i class="o_icon o_icon_content_popup"> </i></a></p>
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_de.properties
index e77dea9718c..ddec53d12a6 100644
--- a/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_de.properties
@@ -1,5 +1,8 @@
 #Mon Mar 02 09:54:03 CET 2009
 rest.doc=Dokumentation
+rest.doc.openapi=Raw JSON
+rest.doc.openapi.experimental=Experimental Dokumentation OpenAPI 3.0
+rest.doc.swagger.ui=SwaggerUI
 rest.title=REST API
 rest.intro=Die REST API macht viele OLAT-Funktionalit\u00E4ten f\u00FCr andere Systeme zug\u00E4nglich. Es ist u.a. m\u00F6glich, Benutzer und Lerngruppen zu verwalten, Kurse zu importieren und Kataloge zu f\u00FChren.
 rest.enabled=REST API Zugang
diff --git a/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_en.properties
index 100ebc204d7..d6ebfb35846 100644
--- a/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/admin/restapi/_i18n/LocalStrings_en.properties
@@ -42,6 +42,9 @@ managed.relation.role=Managed user to user relations
 managed.repo=Managed learning resources
 managed.user.portrait=Managed user portrait
 rest.doc=Documentation
+rest.doc.openapi=Raw JSON
+rest.doc.openapi.experimental=Experimental documentation OpenAPI 3.0
+rest.doc.swagger.ui=SwaggerUI
 rest.enabled=Access REST API
 rest.intro=REST API makes various OLAT features accessible to other systems. It is e.g. possible to manage users and learning groups, import courses, or assemble catalogs.
 rest.on=on
diff --git a/src/main/java/org/olat/restapi/_spring/restApiContext.xml b/src/main/java/org/olat/restapi/_spring/restApiContext.xml
index 030165e4141..0d6d34220ad 100644
--- a/src/main/java/org/olat/restapi/_spring/restApiContext.xml
+++ b/src/main/java/org/olat/restapi/_spring/restApiContext.xml
@@ -8,12 +8,19 @@
                         http://www.springframework.org/schema/context 
                         http://www.springframework.org/schema/context/spring-context.xsd
                         http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
+	<!-- CXF OpenApiFeature -->  
+	<bean id="openApiFeature" class="org.apache.cxf.jaxrs.openapi.OpenApiFeature">
+		<!-- customize some of the properties -->
+	</bean>
 
 	<jaxrs:server address="/" basePackages="org.olat">
 		<jaxrs:providers>
 			<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>
 			<bean class="org.olat.restapi.security.OpenOLATContainerResponseFilter" />
 		</jaxrs:providers>
+		<jaxrs:features>
+			<ref bean="openApiFeature" />
+		</jaxrs:features>
 	</jaxrs:server>
 	<context:component-scan base-package="org.olat.restapi,org.olat.restapi.security,org.olat.restapi.system" />
 	
diff --git a/src/main/java/org/olat/restapi/repository/RepositoryEntriesWebService.java b/src/main/java/org/olat/restapi/repository/RepositoryEntriesWebService.java
index 1b157c25503..645eb712f0e 100644
--- a/src/main/java/org/olat/restapi/repository/RepositoryEntriesWebService.java
+++ b/src/main/java/org/olat/restapi/repository/RepositoryEntriesWebService.java
@@ -31,7 +31,6 @@ import static org.olat.restapi.security.RestSecurityHelper.getRoles;
 import static org.olat.restapi.security.RestSecurityHelper.getUserRequest;
 
 import java.io.File;
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -52,8 +51,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
 
 import org.apache.logging.log4j.Logger;
 import org.olat.basesecurity.OrganisationRoles;
@@ -82,6 +79,11 @@ import org.olat.restapi.support.vo.RepositoryEntryVOes;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.*;
+
 /**
  * Description:<br>
  * This handles the repository entries
@@ -117,46 +119,6 @@ public class RepositoryEntriesWebService {
 	public Response getVersion() {
 		return Response.ok(VERSION).build();
 	}
-
-	/**
-	 * List all entries in the OLAT repository
-	 * @response.representation.200.qname {http://www.example.com}repositoryEntryVO
-	 * @response.representation.200.mediaType text/plain, text/html, application/xml, application/json
-	 * @response.representation.200.doc List all entries in the repository
-	 * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_REPOENTRYVOes}
-	 * @param uriInfo The URI information
-	 * @param httpRequest The HTTP request
-	 * @return
-	 */
-	@GET
-	@Produces({MediaType.TEXT_HTML, MediaType.TEXT_PLAIN})
-	public Response getEntriesText(@Context UriInfo uriInfo, @Context HttpServletRequest httpRequest) {
-		try {
-			// list of courses open for everybody
-			Roles roles = getRoles(httpRequest);
-			Identity identity = getIdentity(httpRequest);
-
-			SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters(identity, roles);
-			List<RepositoryEntry> coursRepos = repositoryManager.genericANDQueryWithRolesRestriction(params, 0, -1, false);
-			
-			StringBuilder sb = new StringBuilder();
-			sb.append("Course List\n");
-			for (RepositoryEntry repoE : coursRepos) {
-				UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
-				URI repoUri = baseUriBuilder.path(RepositoryEntriesWebService.class)
-					.path(repoE.getKey().toString())
-					.build();
-				
-				sb.append("<a href=\"").append(repoUri).append(">")
-					.append(repoE.getDisplayname()).append("(").append(repoE.getKey()).append(")")
-					.append("</a>").append("\n");
-			}
-			
-			return Response.ok(sb.toString()).build();
-		} catch(Exception e) {
-			throw new WebApplicationException(e);
-		}
-	}
 	
 	/**
 	 * List all entries in the OLAT repository
@@ -176,6 +138,17 @@ public class RepositoryEntriesWebService {
 	 */
 	@GET
 	@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+	@ApiResponses(value = {
+		@ApiResponse(responseCode = "200",
+			description = "JVM system properties of a particular host.",
+			content = {
+				@Content(mediaType = "application/json", schema = @Schema(implementation = RepositoryEntryVO[].class)),
+				@Content(mediaType = "application/xml", schema = @Schema(implementation = RepositoryEntryVO[].class))
+			}
+		)}
+	)
+	@Operation(summary = "List all entries in the repository",
+		description = "List all entries in the OpenOLAT repository.")
 	public Response getEntries(@QueryParam("start") @DefaultValue("0") Integer start,
 			@QueryParam("limit") @DefaultValue("25") Integer limit,
 			@QueryParam("managed") Boolean managed, @QueryParam("externalId") String externalId,
-- 
GitLab