This is an automated email from the ASF dual-hosted git repository.

ulyssesyou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new a5ef94a  [KYUUBI #1354] Expose OpenAPI Specifications
a5ef94a is described below

commit a5ef94accc0b6e7557a085512dc8fe062d79375b
Author: Fu Chen <[email protected]>
AuthorDate: Wed Nov 17 12:17:58 2021 +0800

    [KYUUBI #1354] Expose OpenAPI Specifications
    
    <!--
    Thanks for sending a pull request!
    
    Here are some tips for you:
      1. If this is your first time, please read our contributor guidelines: 
https://kyuubi.readthedocs.io/en/latest/community/contributions.html
      2. If the PR is related to an issue in 
https://github.com/apache/incubator-kyuubi/issues, add '[KYUUBI #XXXX]' in your 
PR title, e.g., '[KYUUBI #XXXX] Your PR title ...'.
      3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., 
'[WIP][KYUUBI #XXXX] Your PR title ...'.
    -->
    
    ### _Why are the changes needed?_
    <!--
    Please clarify why the changes are needed. For instance,
      1. If you add a feature, you can talk about the use case of it.
      2. If you fix a bug, you can clarify why it is a bug.
    -->
    
    1. add a new endpoint `/openapi.json` which describes the entire restful 
API of Kyuubi.
    2. add a static web page `/api/v1/swagger-ui`. see 
[swagger-ui](https://github.com/swagger-api/swagger-ui)
    
    ```
    curl http://localhost:10099/openapi.json | jq .
    ```
    
    ```json
    {
      "openapi": "3.0.1",
      "info": {
        "title": "Apache Kyuubi RESTful Api Documentation",
        "description": "Apache Kyuubi RESTful Api Documentation",
        "contact": {
          "name": "Apache Kyuubi Community",
          "url": "https://kyuubi.apache.org/issue_tracking.html";,
          "email": "devkyuubi.apache.org"
        },
        "license": {
          "name": "Apache 2.0",
          "url": "https://www.apache.org/licenses/LICENSE-2.0.html";
        },
        "version": "1.4.0"
      },
      "tags": [
        {
          "name": "Session"
        }
      ],
      "paths": {
        "/api/v1/ping": {
          "get": {
            "operationId": "ping",
            "responses": {
              "default": {
                "description": "default response",
                "content": {
                  "text/plain": {
                    "schema": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "/api/v1/sessions/{sessionHandle}": {
          "get": {
            "tags": [
              "Session"
            ],
            "operationId": "sessionInfo",
            "parameters": [
              {
                "name": "sessionHandle",
                "in": "path",
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            ],
            "responses": {
              "200": {
                "description": "get a session via session handle identifier",
                "content": {
                  "application/json": {}
                }
              }
            }
          },
          "delete": {
            "tags": [
              "Session"
            ],
            "operationId": "closeSession",
            "parameters": [
              {
                "name": "sessionHandle",
                "in": "path",
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            ],
            "responses": {
              "200": {
                "description": "close a Session",
                "content": {
                  "application/json": {}
                }
              }
            }
          }
        },
        "/api/v1/sessions/execPool/statistic": {
          "get": {
            "tags": [
              "Session"
            ],
            "operationId": "execPoolStatistic",
            "responses": {
              "200": {
                "description": "get some statistic info of sessions",
                "content": {
                  "application/json": {}
                }
              }
            }
          }
        },
        "/api/v1/sessions/{sessionHandle}/info/{infoType}": {
          "get": {
            "tags": [
              "Session"
            ],
            "operationId": "getInfo",
            "parameters": [
              {
                "name": "sessionHandle",
                "in": "path",
                "required": true,
                "schema": {
                  "type": "string"
                }
              },
              {
                "name": "infoType",
                "in": "path",
                "required": true,
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            ],
            "responses": {
              "200": {
                "description": "get a information detail via session handle 
identifier and a specific information type",
                "content": {
                  "application/json": {}
                }
              }
            }
          }
        },
        "/api/v1/sessions": {
          "get": {
            "tags": [
              "Session"
            ],
            "operationId": "sessionInfoList",
            "responses": {
              "200": {
                "description": "get all the session list hosted in 
SessionManager",
                "content": {
                  "application/json": {}
                }
              }
            }
          },
          "post": {
            "tags": [
              "Session"
            ],
            "operationId": "openSession",
            "requestBody": {
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/SessionOpenRequest"
                  }
                }
              }
            },
            "responses": {
              "200": {
                "description": "Open(create) a Session",
                "content": {
                  "application/json": {}
                }
              }
            }
          }
        },
        "/api/v1/sessions/count": {
          "get": {
            "tags": [
              "Session"
            ],
            "operationId": "sessionCount",
            "responses": {
              "200": {
                "description": "get open session count",
                "content": {
                  "application/json": {}
                }
              }
            }
          }
        },
        "/api/v1/exception": {
          "get": {
            "operationId": "test",
            "responses": {
              "default": {
                "description": "default response",
                "content": {
                  "text/plain": {}
                }
              }
            }
          }
        },
        "/application.wadl/{path}": {
          "get": {
            "operationId": "getExternalGrammar",
            "parameters": [
              {
                "name": "path",
                "in": "path",
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            ],
            "responses": {
              "default": {
                "description": "default response",
                "content": {
                  "application/xml": {}
                }
              }
            }
          }
        },
        "/application.wadl": {
          "get": {
            "operationId": "getWadl",
            "responses": {
              "default": {
                "description": "default response",
                "content": {
                  "application/vnd.sun.wadl+xml": {},
                  "application/xml": {}
                }
              }
            }
          }
        }
      },
      "components": {
        "schemas": {
          "ExecPoolStatistic": {
            "required": [
              "execPoolSize"
            ],
            "type": "object",
            "properties": {
              "execPoolSize": {
                "type": "integer",
                "format": "int32"
              },
              "execPoolActiveCount": {
                "type": "integer",
                "format": "int32"
              }
            }
          },
          "InfoDetail": {
            "required": [
              "infoType"
            ],
            "type": "object",
            "properties": {
              "infoType": {
                "type": "string"
              },
              "infoValue": {
                "type": "string"
              }
            }
          },
          "HandleIdentifier": {
            "required": [
              "publicId"
            ],
            "type": "object",
            "properties": {
              "publicId": {
                "type": "string",
                "format": "uuid"
              },
              "secretId": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          "SessionHandle": {
            "required": [
              "identifier",
              "protocol"
            ],
            "type": "object",
            "properties": {
              "identifier": {
                "$ref": "#/components/schemas/HandleIdentifier"
              },
              "protocol": {
                "type": "string",
                "enum": [
                  "HIVE_CLI_SERVICE_PROTOCOL_V1",
                  "HIVE_CLI_SERVICE_PROTOCOL_V2",
                  "HIVE_CLI_SERVICE_PROTOCOL_V3",
                  "HIVE_CLI_SERVICE_PROTOCOL_V4",
                  "HIVE_CLI_SERVICE_PROTOCOL_V5",
                  "HIVE_CLI_SERVICE_PROTOCOL_V6",
                  "HIVE_CLI_SERVICE_PROTOCOL_V7",
                  "HIVE_CLI_SERVICE_PROTOCOL_V8",
                  "HIVE_CLI_SERVICE_PROTOCOL_V9",
                  "HIVE_CLI_SERVICE_PROTOCOL_V10"
                ]
              }
            }
          },
          "MapStringString": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "SessionOpenRequest": {
            "required": [
              "configs",
              "protocolVersion",
              "user"
            ],
            "type": "object",
            "properties": {
              "protocolVersion": {
                "type": "integer",
                "format": "int32"
              },
              "user": {
                "type": "string"
              },
              "password": {
                "type": "string"
              },
              "ipAddr": {
                "type": "string"
              },
              "configs": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          },
          "SessionOpenCount": {
            "required": [
              "openSessionCount"
            ],
            "type": "object",
            "properties": {
              "openSessionCount": {
                "type": "integer",
                "format": "int32"
              }
            }
          },
          "SessionDetail": {
            "required": [
              "configs",
              "createTime",
              "user"
            ],
            "type": "object",
            "properties": {
              "user": {
                "type": "string"
              },
              "ipAddr": {
                "type": "string"
              },
              "createTime": {
                "type": "integer",
                "format": "int64"
              },
              "sessionHandle": {
                "$ref": "#/components/schemas/SessionHandle"
              },
              "lastAccessTime": {
                "type": "integer",
                "format": "int64"
              },
              "lastIdleTime": {
                "type": "integer",
                "format": "int64"
              },
              "noOperationTime": {
                "type": "integer",
                "format": "int64"
              },
              "configs": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          },
          "SeqSessionOverview": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionOverview"
            }
          },
          "SessionList": {
            "required": [
              "sessionList"
            ],
            "type": "object",
            "properties": {
              "sessionList": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SessionOverview"
                }
              }
            }
          },
          "SessionOverview": {
            "required": [
              "createTime",
              "user"
            ],
            "type": "object",
            "properties": {
              "user": {
                "type": "string"
              },
              "ipAddr": {
                "type": "string"
              },
              "createTime": {
                "type": "integer",
                "format": "int64"
              },
              "sessionHandle": {
                "$ref": "#/components/schemas/SessionHandle"
              }
            }
          }
        }
      }
    }
    ```
    http://localhost:10099/api/v1/swagger-ui
    
    > Note that this request will redirect to 
http://localhost:10099/swagger-ui-redirected/index.html?url=http://localhost:10099/openapi.json
    
    ![截屏2021-11-09 上午10 43 
49](https://user-images.githubusercontent.com/8537877/141281596-e47f5552-75b9-47d1-8a36-67337cb6eb3a.png)
    
    ### _How was this patch tested?_
    - [ ] Add some test cases that check the changes thoroughly including 
negative and positive cases if possible
    
    - [ ] Add screenshots for manual tests if appropriate
    
    - [ ] [Run 
test](https://kyuubi.readthedocs.io/en/latest/develop_tools/testing.html#running-tests)
 locally before make a pull request
    
    Closes #1363 from cfmcgrady/kyuubi-1354.
    
    Closes #1354
    
    8895d7fc [Fu Chen] typo
    8f5dea9a [Fu Chen] addressed comment
    4d325a45 [Fu Chen] constant MediaType.APPLICATION_JSON
    0d96951e [Fu Chen] RESTful Api -> REST API
    be7d02dc [Fu Chen] add LIABILITY-edl.txt
    4ff1053e [Fu Chen] update dev/dependencyList
    b126f7e6 [Fu Chen] swagger-ui 4.1.0
    a35c2f29 [Fu Chen] addressed comment
    7bde0534 [Fu Chen] update LICENSE-binary
    38397dc2 [Fu Chen] align jackson version
    923f3e7e [Fu Chen] update dev/dependencyList
    7d5338c8 [Fu Chen] remove kyuubi-server static files
    5cb66b33 [Fu Chen] update dev/dependencyList
    e8a8c421 [Fu Chen] fix style
    5b13f69c [Fu Chen] add license and remove .map files
    c04d7a75 [Fu Chen] rebase master
    2388dae8 [Fu Chen] fix style
    8dcbd3db [Fu Chen] add static resource
    4b0d6d0a [Fu Chen] expose OpenAPI
    
    Authored-by: Fu Chen <[email protected]>
    Signed-off-by: ulysses-you <[email protected]>
---
 LICENSE-binary                                     | 21 ++++++++
 dev/dependencyList                                 | 18 +++++++
 kyuubi-server/pom.xml                              | 15 ++++++
 .../apache/kyuubi/server/api/OpenAPIConfig.scala   | 45 +++++++++++++++++
 .../scala/org/apache/kyuubi/server/api/api.scala   | 24 +++++----
 .../kyuubi/server/api/v1/ApiRootResource.scala     | 17 ++++++-
 .../kyuubi/server/api/v1/SessionsResource.scala    | 57 +++++++++++++++++++++-
 .../org/apache/kyuubi/server/api/v1/dto.scala      |  2 +-
 licenses-binary/LICENSE-edl.txt                    | 13 +++++
 pom.xml                                            | 52 ++++++++++++++++++++
 10 files changed, 250 insertions(+), 14 deletions(-)

diff --git a/LICENSE-binary b/LICENSE-binary
index e0a3ef1..1ad1b30 100644
--- a/LICENSE-binary
+++ b/LICENSE-binary
@@ -232,6 +232,11 @@ org.apache.htrace:htrace-core4
 com.fasterxml.jackson.core:jackson-annotations
 com.fasterxml.jackson.core:jackson-core
 com.fasterxml.jackson.core:jackson-databind
+com.fasterxml.jackson.dataformat:jackson-dataformat-yaml
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310
+com.fasterxml.jackson.jaxrs:jackson-jaxrs-base
+com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider
+com.github.swagger-akka-http:swagger-scala-module_*
 org.eclipse.jetty:jetty-http
 org.eclipse.jetty:jetty-io
 org.eclipse.jetty:jetty-security
@@ -241,6 +246,8 @@ org.eclipse.jetty:jetty-util-ajax
 org.eclipse.jetty:jetty-util
 org.apache.thrift:libfb303
 org.apache.thrift:libthrift
+org.webjars:swagger-ui
+org.yaml:snakeyaml
 log4j:log4j
 io.dropwizard.metrics:metrics-core
 io.dropwizard.metrics:metrics-jmx
@@ -251,12 +258,25 @@ io.prometheus:simpleclient
 io.prometheus:simpleclient_common
 io.prometheus:simpleclient_dropwizard
 io.prometheus:simpleclient_servlet
+io.swagger.core.v3:swagger-annotations
+io.swagger.core.v3:swagger-annotations-jakarta
+io.swagger.core.v3:swagger-core
+io.swagger.core.v3:swagger-core-jakarta
+io.swagger.core.v3:swagger-integration
+io.swagger.core.v3:swagger-jaxrs2
+io.swagger.core.v3:swagger-models
+io.swagger.core.v3:swagger-models-jakarta
 org.apache.zookeeper:zookeeper
 
 BSD 3-Clause
 ------------
 org.scala-lang:scala-library
 
+Eclipse Distribution License - v 1.0
+------------------------------------
+com.sun.activation:jakarta.activation
+jakarta.xml.bind:jakarta.xml.bind-api
+
 Eclipse Public License (EPL) 2.0
 --------------------------------
 jakarta.servlet:jakarta.servlet-api 
https://projects.eclipse.org/projects/ee4j.servlet
@@ -271,3 +291,4 @@ org.slf4j:slf4j-api
 org.slf4j:slf4j-log4j12
 org.slf4j:jcl-over-slf4j
 com.github.scopt:scopt_*
+io.github.classgraph:classgraph
diff --git a/dev/dependencyList b/dev/dependencyList
index 58d9a79..e070bf2 100644
--- a/dev/dependencyList
+++ b/dev/dependencyList
@@ -16,6 +16,7 @@
 #
 
 aopalliance-repackaged/2.6.1//aopalliance-repackaged-2.6.1.jar
+classgraph/4.8.95//classgraph-4.8.95.jar
 commons-codec/1.15//commons-codec-1.15.jar
 commons-lang/2.6//commons-lang-2.6.jar
 commons-lang3/3.10//commons-lang3-3.10.jar
@@ -39,14 +40,20 @@ 
htrace-core4/4.1.0-incubating//htrace-core4-4.1.0-incubating.jar
 jackson-annotations/2.11.4//jackson-annotations-2.11.4.jar
 jackson-core/2.11.4//jackson-core-2.11.4.jar
 jackson-databind/2.11.4//jackson-databind-2.11.4.jar
+jackson-dataformat-yaml/2.11.4//jackson-dataformat-yaml-2.11.4.jar
+jackson-datatype-jsr310/2.11.4//jackson-datatype-jsr310-2.11.4.jar
+jackson-jaxrs-base/2.11.4//jackson-jaxrs-base-2.11.4.jar
+jackson-jaxrs-json-provider/2.11.4//jackson-jaxrs-json-provider-2.11.4.jar
 
jackson-module-jaxb-annotations/2.9.9//jackson-module-jaxb-annotations-2.9.9.jar
 jackson-module-paranamer/2.11.4//jackson-module-paranamer-2.11.4.jar
 jackson-module-scala_2.12/2.11.4//jackson-module-scala_2.12-2.11.4.jar
+jakarta.activation/2.0.0//jakarta.activation-2.0.0.jar
 jakarta.annotation-api/1.3.5//jakarta.annotation-api-1.3.5.jar
 jakarta.inject/2.6.1//jakarta.inject-2.6.1.jar
 jakarta.servlet-api/4.0.4//jakarta.servlet-api-4.0.4.jar
 jakarta.validation-api/2.0.2//jakarta.validation-api-2.0.2.jar
 jakarta.ws.rs-api/2.1.6//jakarta.ws.rs-api-2.1.6.jar
+jakarta.xml.bind-api/3.0.0//jakarta.xml.bind-api-3.0.0.jar
 javassist/3.25.0-GA//javassist-3.25.0-GA.jar
 jaxb-api/2.2.11//jaxb-api-2.2.11.jar
 jcl-over-slf4j/1.7.30//jcl-over-slf4j-1.7.30.jar
@@ -83,4 +90,15 @@ 
simpleclient_dropwizard/0.10.0//simpleclient_dropwizard-0.10.0.jar
 simpleclient_servlet/0.10.0//simpleclient_servlet-0.10.0.jar
 slf4j-api/1.7.30//slf4j-api-1.7.30.jar
 slf4j-log4j12/1.7.30//slf4j-log4j12-1.7.30.jar
+snakeyaml/1.26//snakeyaml-1.26.jar
+swagger-annotations-jakarta/2.1.11//swagger-annotations-jakarta-2.1.11.jar
+swagger-annotations/2.1.11//swagger-annotations-2.1.11.jar
+swagger-core-jakarta/2.1.11//swagger-core-jakarta-2.1.11.jar
+swagger-core/2.1.11//swagger-core-2.1.11.jar
+swagger-integration/2.1.11//swagger-integration-2.1.11.jar
+swagger-jaxrs2/2.1.11//swagger-jaxrs2-2.1.11.jar
+swagger-models-jakarta/2.1.11//swagger-models-jakarta-2.1.11.jar
+swagger-models/2.1.11//swagger-models-2.1.11.jar
+swagger-scala-module_2.12/2.5.2//swagger-scala-module_2.12-2.5.2.jar
+swagger-ui/4.1.0//swagger-ui-4.1.0.jar
 zookeeper/3.4.14//zookeeper-3.4.14.jar
diff --git a/kyuubi-server/pom.xml b/kyuubi-server/pom.xml
index ecf336b..d5b8a54 100644
--- a/kyuubi-server/pom.xml
+++ b/kyuubi-server/pom.xml
@@ -203,6 +203,16 @@
         </dependency>
 
         <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-jaxrs2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.swagger-akka-http</groupId>
+            
<artifactId>swagger-scala-module_${scala.binary.version}</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.glassfish.jersey.core</groupId>
             <artifactId>jersey-server</artifactId>
         </dependency>
@@ -433,6 +443,11 @@
             <artifactId>mysql-connector-java</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.webjars</groupId>
+            <artifactId>swagger-ui</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/OpenAPIConfig.scala 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/OpenAPIConfig.scala
new file mode 100644
index 0000000..46539e7
--- /dev/null
+++ 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/OpenAPIConfig.scala
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.server.api
+
+import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource
+import io.swagger.v3.oas.annotations.OpenAPIDefinition
+import io.swagger.v3.oas.annotations.info.{Contact, Info, License}
+import io.swagger.v3.oas.annotations.tags.Tag
+import org.glassfish.jersey.server.ResourceConfig
+
+@OpenAPIDefinition(
+  info = new Info(
+    title = "Apache Kyuubi REST API Documentation",
+    version = "1.4.0",
+    description = "Apache Kyuubi REST API Documentation",
+    contact = new Contact(
+      name = "Apache Kyuubi Community",
+      url = "https://kyuubi.apache.org/issue_tracking.html";,
+      email = "[email protected]"),
+    license = new License(
+      name = "Apache 2.0",
+      url = "https://www.apache.org/licenses/LICENSE-2.0.html";)
+  ),
+  tags = Array(new Tag(name = "Session"))
+)
+class OpenAPIConfig extends ResourceConfig {
+  packages("org.apache.kyuubi.server.api.v1")
+  register(classOf[OpenApiResource]);
+  register(classOf[KyuubiScalaObjectMapper])
+}
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/api.scala 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/api.scala
index 83068e3..6a9bddf 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/api.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/api.scala
@@ -22,8 +22,8 @@ import javax.servlet.http.HttpServletRequest
 import javax.ws.rs.core.Context
 
 import org.eclipse.jetty.server.handler.ContextHandler
-import org.eclipse.jetty.servlet.{ServletContextHandler, ServletHolder}
-import org.glassfish.jersey.server.ServerProperties
+import org.eclipse.jetty.servlet.{DefaultServlet, ServletContextHandler, 
ServletHolder}
+import org.glassfish.jersey.server.ResourceConfig
 import org.glassfish.jersey.servlet.ServletContainer
 
 import org.apache.kyuubi.service.BackendService
@@ -56,17 +56,21 @@ private[api] object BackendServiceProvider {
 private[server] object ApiUtils {
 
   def getServletHandler(backendService: BackendService): ServletContextHandler 
= {
-    val servlet = new ServletHolder(classOf[ServletContainer])
-    servlet.setInitParameter(
-      ServerProperties.PROVIDER_PACKAGES,
-      "org.apache.kyuubi.server.api.v1")
-    servlet.setInitParameter(
-      ServerProperties.PROVIDER_CLASSNAMES,
-      classOf[KyuubiScalaObjectMapper].getName)
+    val openapiConf: ResourceConfig = new OpenAPIConfig
+    val servlet = new ServletHolder(new ServletContainer(openapiConf))
     val handler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS)
     BackendServiceProvider.setBackendService(handler, backendService)
-    handler.setContextPath("/api")
     handler.addServlet(servlet, "/*")
+
+    // install swagger-ui, these static files are copied from
+    // https://github.com/swagger-api/swagger-ui/tree/master/dist
+    val swaggerUI = new ServletHolder("swagger-ui", classOf[DefaultServlet])
+    swaggerUI.setInitParameter("resourceBase",
+      getClass.getClassLoader()
+        .getResource("META-INF/resources/webjars/swagger-ui/4.1.0/")
+        .toExternalForm)
+    swaggerUI.setInitParameter("pathInfoOnly", "true")
+    handler.addServlet(swaggerUI, "/swagger-ui-redirected/*");
     handler
   }
 }
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/ApiRootResource.scala
 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/ApiRootResource.scala
index 751de24..b41290b 100644
--- 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/ApiRootResource.scala
+++ 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/ApiRootResource.scala
@@ -17,13 +17,16 @@
 
 package org.apache.kyuubi.server.api.v1
 
-import com.google.common.annotations.VisibleForTesting
+import java.net.URI
 import javax.ws.rs.{GET, Path, Produces}
 import javax.ws.rs.core.{MediaType, Response}
 
+import com.google.common.annotations.VisibleForTesting
+
+import org.apache.kyuubi.server.KyuubiServer
 import org.apache.kyuubi.server.api.ApiRequestContext
 
-@Path("/v1")
+@Path("/api/v1")
 private[v1] class ApiRootResource extends ApiRequestContext {
 
   @GET
@@ -43,4 +46,14 @@ private[v1] class ApiRootResource extends ApiRequestContext {
     Response.ok().build()
   }
 
+  @GET
+  @Path("swagger-ui")
+  @Produces(Array(MediaType.TEXT_HTML))
+  def swaggerUi(): Response = {
+    val serverIP = 
KyuubiServer.kyuubiServer.frontendServices.head.connectionUrl
+    val swaggerUi =
+      
s"http://$serverIP/swagger-ui-redirected/index.html?url=http://$serverIP/openapi.json";
+    Response.temporaryRedirect(new URI(swaggerUi)).build()
+  }
+
 }
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala
 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala
index 7ac06dc..30c5532 100644
--- 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala
+++ 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala
@@ -19,11 +19,15 @@ package org.apache.kyuubi.server.api.v1
 
 import java.util.UUID
 import javax.ws.rs._
+import javax.ws.rs.{Consumes, DELETE, GET, Path, PathParam, POST, Produces}
 import javax.ws.rs.core.{MediaType, Response}
 
 import scala.collection.JavaConverters._
 import scala.util.control.NonFatal
 
+import io.swagger.v3.oas.annotations.media.Content
+import io.swagger.v3.oas.annotations.responses.ApiResponse
+import io.swagger.v3.oas.annotations.tags.Tag
 import org.apache.hive.service.rpc.thrift.{TGetInfoType, TProtocolVersion}
 
 import org.apache.kyuubi.Utils.error
@@ -32,19 +36,34 @@ import org.apache.kyuubi.operation.OperationHandle
 import org.apache.kyuubi.server.api.ApiRequestContext
 import org.apache.kyuubi.session.SessionHandle
 
+@Tag(name = "Session")
 @Produces(Array(MediaType.APPLICATION_JSON))
 private[v1] class SessionsResource extends ApiRequestContext {
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "get all the session list hosted in SessionManager"
+  )
   @GET
   def sessionInfoList(): SessionList = {
     SessionList(
       backendService.sessionManager.getSessionList().asScala.map {
         case (handle, session) =>
           SessionOverview(session.user, session.ipAddress, session.createTime, 
handle)
-      }.toList
+      }.toSeq
     )
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "get a session via session handle identifier"
+  )
   @GET
   @Path("{sessionHandle}")
   def sessionInfo(@PathParam("sessionHandle") sessionHandleStr: String): 
SessionDetail = {
@@ -60,6 +79,14 @@ private[v1] class SessionsResource extends ApiRequestContext 
{
     }
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description =
+      "get a information detail via session handle identifier and a specific 
information type"
+  )
   @GET
   @Path("{sessionHandle}/info/{infoType}")
   def getInfo(@PathParam("sessionHandle") sessionHandleStr: String,
@@ -77,12 +104,26 @@ private[v1] class SessionsResource extends 
ApiRequestContext {
     }
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "Get the current open session count"
+  )
   @GET
   @Path("count")
   def sessionCount(): SessionOpenCount = {
     SessionOpenCount(backendService.sessionManager.getOpenSessionCount)
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "Get statistic info of background executors"
+  )
   @GET
   @Path("execPool/statistic")
   def execPoolStatistic(): ExecPoolStatistic = {
@@ -90,6 +131,13 @@ private[v1] class SessionsResource extends 
ApiRequestContext {
       backendService.sessionManager.getActiveCount)
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "Open(create) a session"
+  )
   @POST
   @Consumes(Array(MediaType.APPLICATION_JSON))
   def openSession(request: SessionOpenRequest): SessionHandle = {
@@ -101,6 +149,13 @@ private[v1] class SessionsResource extends 
ApiRequestContext {
       request.configs)
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_JSON
+    )),
+    description = "Close a session"
+  )
   @DELETE
   @Path("{sessionHandle}")
   def closeSession(@PathParam("sessionHandle") sessionHandleStr: String): 
Response = {
diff --git 
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/dto.scala 
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/dto.scala
index 90fcde3..4abd17a 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/dto.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/dto.scala
@@ -23,7 +23,7 @@ case class SessionOpenCount(openSessionCount: Int)
 
 case class ExecPoolStatistic(execPoolSize: Int, execPoolActiveCount: Int)
 
-case class SessionList(sessionList: List[SessionOverview])
+case class SessionList(sessionList: Seq[SessionOverview])
 
 case class SessionOverview(
   user: String,
diff --git a/licenses-binary/LICENSE-edl.txt b/licenses-binary/LICENSE-edl.txt
new file mode 100644
index 0000000..6e96b02
--- /dev/null
+++ b/licenses-binary/LICENSE-edl.txt
@@ -0,0 +1,13 @@
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this 
list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright notice, 
this list of conditions and the following disclaimer in the documentation 
and/or other materials provided with the distribution.
+- Neither the name of the Eclipse Foundation, Inc. nor the names of its 
contributors may be used to endorse or promote products derived from this 
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFI [...]
diff --git a/pom.xml b/pom.xml
index 87d80a9..c7eadb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,6 +129,9 @@
         
<spark.archive.name>spark-${spark.version}-bin-hadoop${hadoop.binary.version}.tgz</spark.archive.name>
         
<spark.archive.mirror>https://archive.apache.org/dist/spark/spark-${spark.version}</spark.archive.mirror>
         <spark.archive.download.skip>false</spark.archive.download.skip>
+        <swagger.version>2.1.11</swagger.version>
+        <swagger.scala.module.version>2.5.2</swagger.scala.module.version>
+        <swagger-ui.version>4.1.0</swagger-ui.version>
         <zookeeper.version>3.4.14</zookeeper.version>
 
         <!-- only apply to kyuubi-hive-jdbc module -->
@@ -569,6 +572,30 @@
             </dependency>
 
             <dependency>
+                <groupId>com.fasterxml.jackson.dataformat</groupId>
+                <artifactId>jackson-dataformat-yaml</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fasterxml.jackson.datatype</groupId>
+                <artifactId>jackson-datatype-jsr310</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fasterxml.jackson.jaxrs</groupId>
+                <artifactId>jackson-jaxrs-base</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fasterxml.jackson.jaxrs</groupId>
+                <artifactId>jackson-jaxrs-json-provider</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>com.fasterxml.jackson.module</groupId>
                 
<artifactId>jackson-module-scala_${scala.binary.version}</artifactId>
                 <version>${jackson.version}</version>
@@ -591,6 +618,19 @@
             </dependency>
 
             <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-jaxrs2</artifactId>
+                <version>${swagger.version}</version>
+            </dependency>
+
+            <!-- ref: 
https://github.com/swagger-akka-http/swagger-scala-module -->
+            <dependency>
+                <groupId>com.github.swagger-akka-http</groupId>
+                
<artifactId>swagger-scala-module_${scala.binary.version}</artifactId>
+                <version>${swagger.scala.module.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>io.netty</groupId>
                 <artifactId>netty-all</artifactId>
                 <version>${netty.version}</version>
@@ -897,6 +937,18 @@
                 <artifactId>mysql-connector-java</artifactId>
                 <version>${mysql.jdbc.version}</version>
             </dependency>
+
+            <!--
+              1. This library only contains swagger-ui static resource 
(.html/.css/.js/.png), for more detail, see
+                 https://github.com/swagger-api/swagger-ui/blob/master/dist/
+              2. Note that when trying to upgrade swagger-ui, we should also 
update the version in the file(
+                 
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/api.scala).
+            -->
+            <dependency>
+                <groupId>org.webjars</groupId>
+                <artifactId>swagger-ui</artifactId>
+                <version>${swagger-ui.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

Reply via email to