diff --git a/s2http/build.sbt b/s2http/build.sbt
index ef020cd9..c094a6eb 100644
--- a/s2http/build.sbt
+++ b/s2http/build.sbt
@@ -18,6 +18,7 @@
*/
lazy val akkaHttpVersion = "10.1.6"
lazy val akkaVersion = "2.5.19"
+lazy val swaggerUiWebjarsVersion = "3.20.9"
name := "s2http"
@@ -32,6 +33,8 @@ libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion,
"com.typesafe.akka" %% "akka-stream" % akkaVersion,
+ "org.webjars" % "swagger-ui" % swaggerUiWebjarsVersion,
+
"com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % Test,
"com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test,
diff --git a/s2http/src/main/resources/META-INF/resources/s2http/swagger/index.html b/s2http/src/main/resources/META-INF/resources/s2http/swagger/index.html
new file mode 100644
index 00000000..68592576
--- /dev/null
+++ b/s2http/src/main/resources/META-INF/resources/s2http/swagger/index.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+ Swagger UI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/s2http/src/main/resources/META-INF/resources/s2http/swagger/v0/swagger.json b/s2http/src/main/resources/META-INF/resources/s2http/swagger/v0/swagger.json
new file mode 100644
index 00000000..a8d8bdaf
--- /dev/null
+++ b/s2http/src/main/resources/META-INF/resources/s2http/swagger/v0/swagger.json
@@ -0,0 +1,1273 @@
+{
+ "swagger":"2.0",
+ "info":{
+ "description":"This is S2Graph HTTP Server.",
+ "version":"0.2",
+ "title":"S2Graph HTTP Server",
+ "contact":{
+ "email":"dev@s2graph.incubator.apache.org"
+ },
+ "license":{
+ "name":"Apache 2.0",
+ "url":"http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "tags":[
+ {
+ "name":"s2graph",
+ "description":"S2Graph HTTP Server",
+ "externalDocs":{
+ "description":"Find out more about Apache S2Graph",
+ "url":"http://s2graph.incubator.apache.org/"
+ }
+ }
+ ],
+ "schemes":[
+ "http",
+ "https"
+ ],
+ "paths":{
+ "/admin/createService":{
+ "post":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Create a service",
+ "description":"",
+ "operationId":"createService",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Service",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/Service"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successfully created",
+ "schema":{
+ "$ref":"#/definitions/ServiceResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/createLabel":{
+ "post":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Create a label",
+ "description":"",
+ "operationId":"createLabel",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Service",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/Label"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/LabelResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/addIndex":{
+ "post":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Add indices to a label",
+ "description":"",
+ "operationId":"addIndex",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Label indices to add",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/LabelIndices"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/LabelResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/getLabel/{name}":{
+ "get":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Get general information on a label",
+ "description":"",
+ "operationId":"getLabelByName",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"name",
+ "description":"Label name",
+ "required":true,
+ "type":"string"
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/LabelResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/deleteLabelReally/{name}":{
+ "put":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Delete a label",
+ "description":"",
+ "operationId":"deleteLabelByName",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"name",
+ "description":"Label name",
+ "required":true,
+ "type":"string"
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/Label"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/addProp/{name}":{
+ "post":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Add a new property to the label",
+ "description":"",
+ "operationId":"addPropToLabel",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"name",
+ "description":"Label name",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Property definition",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/PropertyDefinition"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/LabelResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/admin/createServiceColumn":{
+ "post":{
+ "tags":[
+ "admin"
+ ],
+ "summary":"Add a new property to the label",
+ "description":"",
+ "operationId":"createServiceColumn",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Service column definition",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/ServiceColumn"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/LabelResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/vertex/insert/{service}/{column}":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Create new vertices.",
+ "description":"",
+ "operationId":"insertVertices",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"service",
+ "description":"Service name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"path",
+ "name":"column",
+ "description":"Column name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/VerticesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/VerticesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/vertex/delete/{service}/{column}":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Delete vertices.",
+ "description":"",
+ "operationId":"deleteVertices",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"service",
+ "description":"Service name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"path",
+ "name":"column",
+ "description":"Column name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/VerticesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/VerticesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/vertex/deleteAll/{service}/{column}":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Delete all vertices.",
+ "description":"",
+ "operationId":"deleteAllVertices",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"path",
+ "name":"service",
+ "description":"Service name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"path",
+ "name":"column",
+ "description":"Column name.",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/VerticesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/VerticesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/edge/insert":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Create new edges.",
+ "description":"",
+ "operationId":"insertEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/edge/delete":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Delete edges.",
+ "description":"",
+ "operationId":"deleteEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/edge/update":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Update edges.",
+ "description":"",
+ "operationId":"updateEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/edge/increment":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Return edges with incremented values",
+ "description":"Works like update, other than it returns the incremented value and not the old value.",
+ "operationId":"incrementEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/mutate/edge/deleteAll":{
+ "post":{
+ "tags":[
+ "mutate"
+ ],
+ "summary":"Delete all edges.",
+ "description":"",
+ "operationId":"deleteAllEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "201":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/graphs/getVertices":{
+ "post":{
+ "tags":[
+ "query"
+ ],
+ "summary":"Select vertices.",
+ "description":"",
+ "operationId":"getVertices",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get vertices.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/VerticesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/VerticesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/graphs/checkEdges":{
+ "post":{
+ "tags":[
+ "query"
+ ],
+ "summary":"Return edge for given vertex pair only if edge exist.",
+ "description":"This is more general way to check edge existence between any given vertex pairs comparing using _to on query parameter.",
+ "operationId":"checkEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ },
+ "/graphs/getEdges":{
+ "post":{
+ "tags":[
+ "query"
+ ],
+ "summary":"Select edges.",
+ "description":"",
+ "operationId":"getEdges",
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "parameters":[
+ {
+ "in":"body",
+ "name":"body",
+ "description":"Request to get edges.",
+ "required":true,
+ "schema":{
+ "$ref":"#/definitions/EdgesRequest"
+ }
+ }
+ ],
+ "responses":{
+ "200":{
+ "description":"Successful operation",
+ "schema":{
+ "$ref":"#/definitions/EdgesResponse"
+ }
+ },
+ "400":{
+ "description":"Bad request"
+ }
+ }
+ }
+ }
+ },
+ "definitions":{
+ "Service":{
+ "type":"object",
+ "required":[
+ "serviceName"
+ ],
+ "properties":{
+ "serviceName":{
+ "description":"User defined namespace",
+ "type":"string"
+ },
+ "cluster":{
+ "description":"Zookeeper quorum address",
+ "type":"string"
+ },
+ "hTableName":{
+ "description":"HBase table name",
+ "type":"string"
+ },
+ "hTableTTL":{
+ "description":"Time to live setting for data",
+ "type":"integer"
+ },
+ "preSplitSize":{
+ "description":"Factor for the table pre-split size",
+ "type":"integer"
+ }
+ }
+ },
+ "Label":{
+ "type":"object",
+ "required":[
+ "label",
+ "srcServiceName",
+ "srcColumnName",
+ "srcColumnType",
+ "tgtColumnName",
+ "tgtColumnType"
+ ],
+ "properties":{
+ "label":{
+ "description":"Name of the relation",
+ "type":"string"
+ },
+ "srcServiceName":{
+ "description":"Source column’s service",
+ "type":"string"
+ },
+ "srcColumnName":{
+ "description":"Source column’s name",
+ "type":"string"
+ },
+ "srcColumnType":{
+ "description":"Source column’s data type",
+ "type":"string",
+ "enum":[
+ "long",
+ "integer",
+ "string"
+ ]
+ },
+ "tgtServiceName":{
+ "description":"Target column’s service",
+ "type":"string"
+ },
+ "tgtColumnName":{
+ "description":"Target column’s name",
+ "type":"string"
+ },
+ "tgtColumnType":{
+ "description":"Target column’s data type",
+ "type":"string",
+ "enum":[
+ "long",
+ "integer",
+ "string"
+ ]
+ },
+ "indices":{
+ "description":"Index definitions on the label",
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/IndexDefinition"
+ }
+ },
+ "props":{
+ "description":"Property definitions on the label",
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/PropertyDefinition"
+ }
+ },
+ "isDirected":{
+ "description":"Wether the label is directed or undirected",
+ "type":"integer"
+ },
+ "serviceName":{
+ "description":"Which service the label belongs to",
+ "type":"string"
+ },
+ "hTableName":{
+ "description":"A dedicated HBase table to your Label",
+ "type":"string"
+ },
+ "hTableTTL":{
+ "description":"Data time to live setting",
+ "type":"integer"
+ },
+ "consistencyLevel":{
+ "description":"If set to ‘strong’, only one edge is alowed between a pair of source/ target vertices. Set to ‘weak’, and multiple-edge is supported",
+ "type":"string",
+ "enum":[
+ "weak",
+ "strong"
+ ]
+ }
+ }
+ },
+ "LabelIndices":{
+ "type":"object",
+ "required":[
+ "label"
+ ],
+ "properties":{
+ "label":{
+ "description":"Name of the relation",
+ "type":"string"
+ },
+ "indices":{
+ "description":"Index definitions on the label",
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/IndexDefinition"
+ }
+ }
+ }
+ },
+ "IndexDefinition":{
+ "type":"object",
+ "required":[
+ "name",
+ "propNames"
+ ],
+ "properties":{
+ "name":{
+ "description":"Label index name",
+ "type":"string"
+ },
+ "propNames":{
+ "description":"Label property names used by the index",
+ "type":"array",
+ "items":{
+ "type":"string"
+ }
+ },
+ "dir":{
+ "type":"string"
+ },
+ "options":{
+ "type":"string"
+ }
+ }
+ },
+ "PropertyDefinition":{
+ "type":"object",
+ "required":[
+ "name",
+ "dataType"
+ ],
+ "properties":{
+ "name":{
+ "description":"Label property name",
+ "type":"string"
+ },
+ "dataType":{
+ "description":"Data type of the label property",
+ "type":"string"
+ },
+ "defaultValue":{
+ "description":"Default value of the label property",
+ "type":"string"
+ }
+ }
+ },
+ "ServiceResponse":{
+ "type":"object",
+ "required":[
+ "status"
+ ],
+ "properties":{
+ "status":{
+ "description":"Status",
+ "type":"string"
+ },
+ "messasge":{
+ "$ref":"#/definitions/ServiceResponseMessage"
+ }
+ }
+ },
+ "ServiceResponseMessage":{
+ "type":"object",
+ "required":[
+ "id",
+ "name"
+ ],
+ "properties":{
+ "id":{
+ "description":"Message ID",
+ "type":"string"
+ },
+ "name":{
+ "description":"User defined namespace",
+ "type":"string"
+ },
+ "accessToken":{
+ "description":"Access token",
+ "type":"string"
+ },
+ "cluster":{
+ "description":"Zookeeper quorum address",
+ "type":"string"
+ },
+ "hTableName":{
+ "description":"HBase table name",
+ "type":"string"
+ },
+ "hTableTTL":{
+ "description":"Time to live setting for data",
+ "type":"integer"
+ },
+ "preSplitSize":{
+ "description":"Factor for the table pre-split size",
+ "type":"integer"
+ }
+ }
+ },
+ "LabelResponse":{
+ "type":"object",
+ "required":[
+ "status"
+ ],
+ "properties":{
+ "status":{
+ "description":"Status",
+ "type":"string"
+ },
+ "messasge":{
+ "$ref":"#/definitions/LabelResponseMessage"
+ }
+ }
+ },
+ "LabelResponseMessage":{
+ "type":"object",
+ "required":[
+ "labelName"
+ ],
+ "properties":{
+ "labelName":{
+ "description":"The label name",
+ "type":"string"
+ },
+ "from":{
+ "$ref":"#/definitions/ServiceLabelColumn"
+ },
+ "to":{
+ "$ref":"#/definitions/ServiceLabelColumn"
+ },
+ "isDirected":{
+ "type":"boolean"
+ },
+ "serviceName":{
+ "description":"User defined namespace",
+ "type":"string"
+ },
+ "consistencyLevel":{
+ "description":"If set to ‘strong’, only one edge is alowed between a pair of source/ target vertices. Set to ‘weak’, and multiple-edge is supported",
+ "type":"string",
+ "enum":[
+ "weak",
+ "strong"
+ ]
+ },
+ "schemaVersion":{
+ "type":"string"
+ },
+ "isAsync":{
+ "type":"boolean"
+ },
+ "compressionAlgorithm":{
+ "type":"string"
+ },
+ "defaultIndex":{
+ "$ref":"#/definitions/IndexDefinition"
+ },
+ "extraIndex":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/IndexDefinition"
+ }
+ },
+ "metaProps":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/PropertyDefinition"
+ }
+ },
+ "options":{
+ "type":"string"
+ }
+ }
+ },
+ "ServiceColumn":{
+ "type":"object",
+ "properties":{
+ "serviceName":{
+ "type":"string"
+ },
+ "columnName":{
+ "type":"string"
+ },
+ "columnType":{
+ "type":"string"
+ },
+ "props":{
+ "description":"Property definitions",
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/PropertyDefinition"
+ }
+ }
+ }
+ },
+ "ServiceLabelColumn":{
+ "type":"object",
+ "properties":{
+ "from":{
+ "type":"string"
+ },
+ "serviceName":{
+ "type":"string"
+ },
+ "columnName":{
+ "type":"string"
+ },
+ "columnType":{
+ "type":"string"
+ }
+ }
+ },
+ "VerticesRequest":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/Vertex"
+ }
+ },
+ "VerticesResponse":{
+ "type":"object",
+ "properties":{
+ "size":{
+ "type":"integer"
+ },
+ "degrees":{
+ "type":"array",
+ "items":{
+ }
+ },
+ "results":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/Vertex"
+ }
+ },
+ "impressionId":{
+ "type":"integer"
+ }
+ }
+ },
+ "EdgesRequest":{
+ "type":"object",
+ "properties":{
+ "srcVertices":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/Vertex"
+ }
+ },
+ "steps":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/Step"
+ }
+ }
+ }
+ },
+ "EdgesResponse":{
+ "type":"object",
+ "properties":{
+ "size":{
+ "type":"integer"
+ },
+ "degrees":{
+ "type":"array",
+ "items":{
+ }
+ },
+ "results":{
+ "type":"array",
+ "items":{
+ "$ref":"#/definitions/Edge"
+ }
+ },
+ "impressionId":{
+ "type":"integer"
+ }
+ }
+ },
+ "Vertex":{
+ "type":"object",
+ "properties":{
+ "id":{
+ "type":"string"
+ },
+ "serviceName":{
+ "type":"string"
+ },
+ "columnName":{
+ "type":"string"
+ },
+ "timestamp":{
+ "type":"integer"
+ },
+ "props":{
+ "type":"object"
+ }
+ }
+ },
+ "Step":{
+ "type":"object",
+ "properties":{
+ "label":{
+ "type":"string"
+ },
+ "direction":{
+ "type":"string"
+ },
+ "offset":{
+ "type":"integer"
+ },
+ "limit":{
+ "type":"integer"
+ },
+ "duplicate":{
+ "type":"string"
+ }
+ }
+ },
+ "Edge":{
+ "type":"object",
+ "properties":{
+ "from":{
+ "type":"string"
+ },
+ "to":{
+ "type":"string"
+ },
+ "direction":{
+ "type":"string"
+ },
+ "label":{
+ "type":"string"
+ },
+ "score":{
+ "type":"string"
+ },
+ "cacheRemain":{
+ "type":"string"
+ },
+ "_timestamp":{
+ "type":"string"
+ },
+ "timestamp":{
+ "type":"string"
+ },
+ "props":{
+ "$ref":"#/definitions/EdgeProperties"
+ }
+ }
+ },
+ "EdgeProperties":{
+ "type":"object",
+ "properties":{
+ "_timestamp":{
+ "type":"integer"
+ },
+ "time":{
+ "type":"integer"
+ },
+ "weight":{
+ "type":"integer"
+ },
+ "is_hidden":{
+ "type":"boolean"
+ },
+ "is_blocked":{
+ "type":"boolean"
+ }
+ }
+ }
+ },
+ "externalDocs":{
+ "description":"Find out more about Apache S2Graph",
+ "url":"http://s2graph.incubator.apache.org/"
+ }
+}
diff --git a/s2http/src/main/scala/org/apache/s2graph/http/Server.scala b/s2http/src/main/scala/org/apache/s2graph/http/Server.scala
index e70a1829..fb276f89 100644
--- a/s2http/src/main/scala/org/apache/s2graph/http/Server.scala
+++ b/s2http/src/main/scala/org/apache/s2graph/http/Server.scala
@@ -53,11 +53,15 @@ object Server extends App
val port = sys.props.get("http.port").fold(8000)(_.toInt)
val interface = sys.props.get("http.interface").fold("0.0.0.0")(identity)
+ val SwaggerIndexPage = "index.html"
+ val SwaggerIndexResPath = s"META-INF/resources/s2http/swagger/$SwaggerIndexPage"
+ val SwaggerWebJarBaseResPath = "META-INF/resources/webjars/swagger-ui/3.20.9/"
+
val startAt = System.currentTimeMillis()
def uptime = System.currentTimeMillis() - startAt
- def serverHealth = s"""{ "port": ${port}, "interface": "${interface}", "started_at": ${Instant.ofEpochMilli(startAt)}, "uptime": "${uptime} millis" """
+ def serverHealth = s"""{ "port": ${port}, "interface": "${interface}", "started_at": "${Instant.ofEpochMilli(startAt)}", "uptime": "${uptime} millis" }"""
def health = HttpResponse(status = StatusCodes.OK, entity = HttpEntity(ContentTypes.`application/json`, serverHealth))
@@ -67,6 +71,20 @@ object Server extends App
pathPrefix("mutate")(mutateRoute),
pathPrefix("admin")(adminRoute),
pathPrefix("graphql")(graphqlRoute),
+ pathPrefix("api-docs") {
+ pathEnd {
+ redirect("/api-docs/", StatusCodes.TemporaryRedirect)
+ } ~
+ path(Segments) { segs => {
+ val resPath = segs match {
+ case Nil | SwaggerIndexPage :: Nil => SwaggerIndexResPath
+ case "s2http" :: rest => "META-INF/resources/s2http/" + rest.mkString("/")
+ case _ => SwaggerWebJarBaseResPath + segs.mkString("/")
+ }
+ getFromResource(resPath)
+ }
+ }
+ },
get(complete(health))
)