response =
+ vertexApi.deleteVertexWithHttpInfo(graphId, label, primaryKey);
+ return Result.fromResponse(response);
+ } catch (ApiException e) {
+ e.printStackTrace();
+ return Result.fromException(e);
+ }
+ }
+
+ /**
+ * Closes this resource, relinquishing any underlying resources.
+ * This method is invoked automatically on objects managed by the
+ * {@code try}-with-resources statement.
+ *
+ * While this interface method is declared to throw {@code
+ * Exception}, implementers are strongly encouraged to
+ * declare concrete implementations of the {@code close} method to
+ * throw more specific exceptions, or to throw no exception at all
+ * if the close operation cannot fail.
+ *
+ *
Cases where the close operation may fail require careful
+ * attention by implementers. It is strongly advised to relinquish
+ * the underlying resources and to internally mark the
+ * resource as closed, prior to throwing the exception. The {@code
+ * close} method is unlikely to be invoked more than once and so
+ * this ensures that the resources are released in a timely manner.
+ * Furthermore it reduces problems that could arise when the resource
+ * wraps, or is wrapped, by another resource.
+ *
+ *
Implementers of this interface are also strongly advised
+ * to not have the {@code close} method throw {@link
+ * InterruptedException}.
+ *
+ * This exception interacts with a thread's interrupted status,
+ * and runtime misbehavior is likely to occur if an {@code
+ * InterruptedException} is {@linkplain Throwable#addSuppressed
+ * suppressed}.
+ *
+ * More generally, if it would cause problems for an
+ * exception to be suppressed, the {@code AutoCloseable.close}
+ * method should not throw it.
+ *
+ *
Note that unlike the {@link Closeable#close close}
+ * method of {@link Closeable}, this {@code close} method
+ * is not required to be idempotent. In other words,
+ * calling this {@code close} method more than once may have some
+ * visible side effect, unlike {@code Closeable.close} which is
+ * required to have no effect if called more than once.
+ *
+ * However, implementers of this interface are strongly encouraged
+ * to make their {@code close} methods idempotent.
+ *
+ * @throws Exception if this resource cannot be closed
+ */
+ @Override
+ public void close() throws Exception {}
+}
diff --git a/flex/interactive/sdk/java/src/test/java/com/alibaba/graphscope/interactive/client/DriverTest.java b/flex/interactive/sdk/java/src/test/java/com/alibaba/graphscope/interactive/client/DriverTest.java
new file mode 100644
index 000000000000..2fb3ae4ae3c7
--- /dev/null
+++ b/flex/interactive/sdk/java/src/test/java/com/alibaba/graphscope/interactive/client/DriverTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2022 Alibaba Group Holding Limited.
+ *
+ * Licensed 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 com.alibaba.graphscope.interactive.client;
+
+import com.alibaba.graphscope.interactive.client.common.Result;
+import com.alibaba.graphscope.interactive.openapi.model.*;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.tinkerpop.gremlin.driver.Client;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+@TestMethodOrder(OrderAnnotation.class)
+public class DriverTest {
+ private static final Logger logger = Logger.getLogger(DriverTest.class.getName());
+
+ private static Driver driver;
+ private static Session session;
+ private static org.neo4j.driver.Session neo4jSession;
+ private static Client gremlinClient;
+ private static String graphId;
+ private static String jobId;
+ private static String procedureId;
+
+ @BeforeAll
+ public static void beforeClass() {
+ String interactiveEndpoint = System.getProperty("interactive.endpoint", "localhost:7777");
+ driver = Driver.connect(interactiveEndpoint);
+ session = driver.session();
+ String neo4jEndpoint = driver.getNeo4jEndpoint();
+ if (neo4jEndpoint != null) {
+ neo4jSession = driver.getNeo4jSession();
+ }
+ Pair gremlinEndpoint = driver.getGremlinEndpoint();
+ if (gremlinEndpoint != null) {
+ gremlinClient = driver.getGremlinClient();
+ }
+ logger.info("Finish setup");
+ }
+
+ @Test
+ @Order(1)
+ public void test0CreateGraph() {
+ CreateGraphRequest graph = new CreateGraphRequest();
+ graph.setName("testGraph");
+ graph.setDescription("a simple test graph");
+ CreateGraphSchemaRequest schema = new CreateGraphSchemaRequest();
+ {
+ CreateVertexType vertexType = new CreateVertexType();
+ vertexType.setTypeName("person");
+ List propertyMetaList = new ArrayList<>();
+ {
+ CreatePropertyMeta propertyMeta = new CreatePropertyMeta();
+ propertyMeta.setPropertyName("id");
+ propertyMeta.setPropertyType(
+ new GSDataType(
+ new PrimitiveType()
+ .primitiveType(
+ PrimitiveType.PrimitiveTypeEnum.SIGNED_INT64)));
+ propertyMetaList.add(propertyMeta);
+ }
+ {
+ CreatePropertyMeta propertyMeta = new CreatePropertyMeta();
+ propertyMeta.setPropertyName("name");
+ propertyMeta.setPropertyType(
+ new GSDataType(
+ (new StringType()
+ .string(
+ new StringTypeString(
+ new LongText().longText(""))))));
+ logger.info("json: " + propertyMeta.toJson());
+ propertyMetaList.add(propertyMeta);
+ }
+ {
+ // age
+ CreatePropertyMeta propertyMeta = new CreatePropertyMeta();
+ propertyMeta.setPropertyName("age");
+ propertyMeta.setPropertyType(
+ new GSDataType(
+ new PrimitiveType()
+ .primitiveType(
+ PrimitiveType.PrimitiveTypeEnum.SIGNED_INT32)));
+ propertyMetaList.add(propertyMeta);
+ }
+ vertexType.setProperties(propertyMetaList);
+ vertexType.addPrimaryKeysItem("id");
+ schema.addVertexTypesItem(vertexType);
+ }
+ {
+ CreateEdgeType edgeType = new CreateEdgeType();
+ edgeType.setTypeName("knows");
+ List propertyMetaList = new ArrayList<>();
+ {
+ CreatePropertyMeta propertyMeta = new CreatePropertyMeta();
+ propertyMeta.setPropertyName("weight");
+ propertyMeta.setPropertyType(
+ new GSDataType(
+ new PrimitiveType()
+ .primitiveType(PrimitiveType.PrimitiveTypeEnum.DOUBLE)));
+ propertyMetaList.add(propertyMeta);
+ }
+ edgeType.setProperties(propertyMetaList);
+ BaseEdgeTypeVertexTypePairRelationsInner relationShip =
+ new BaseEdgeTypeVertexTypePairRelationsInner();
+ relationShip.setSourceVertex("person");
+ relationShip.setDestinationVertex("person");
+ relationShip.relation(
+ BaseEdgeTypeVertexTypePairRelationsInner.RelationEnum.MANY_TO_MANY);
+ edgeType.addVertexTypePairRelationsItem(relationShip);
+ schema.addEdgeTypesItem(edgeType);
+ }
+ graph.setSchema(schema);
+ Result rep = session.createGraph(graph);
+ assertOk(rep);
+ graphId = rep.getValue().getGraphId();
+ logger.info("graphId: " + graphId);
+ }
+
+ @Test
+ @Order(2)
+ public void test1BulkLoading() {
+ SchemaMapping schemaMapping = new SchemaMapping();
+ schemaMapping.setGraph(graphId);
+ {
+ SchemaMappingLoadingConfig loadingConfig = new SchemaMappingLoadingConfig();
+ loadingConfig.setImportOption(SchemaMappingLoadingConfig.ImportOptionEnum.INIT);
+ loadingConfig.setFormat(new SchemaMappingLoadingConfigFormat().type("csv"));
+ schemaMapping.setLoadingConfig(loadingConfig);
+ }
+ {
+ // get env var FLEX_DATA_DIR
+ if (System.getenv("FLEX_DATA_DIR") == null) {
+ logger.info("FLEX_DATA_DIR is not set");
+ return;
+ }
+ String personPath = System.getenv("FLEX_DATA_DIR") + "/person.csv";
+ String knowsPath = System.getenv("FLEX_DATA_DIR") + "/person_knows_person.csv";
+ {
+ VertexMapping vertexMapping = new VertexMapping();
+ vertexMapping.setTypeName("person");
+ vertexMapping.addInputsItem(personPath);
+ schemaMapping.addVertexMappingsItem(vertexMapping);
+ }
+ {
+ EdgeMapping edgeMapping = new EdgeMapping();
+ edgeMapping.setTypeTriplet(
+ new EdgeMappingTypeTriplet()
+ .edge("knows")
+ .sourceVertex("person")
+ .destinationVertex("person"));
+ edgeMapping.addInputsItem(knowsPath);
+ schemaMapping.addEdgeMappingsItem(edgeMapping);
+ }
+ }
+ Result rep = session.bulkLoading(graphId, schemaMapping);
+ assertOk(rep);
+ jobId = rep.getValue().getJobId();
+ logger.info("job id: " + jobId);
+ }
+
+ @Test
+ @Order(3)
+ public void test2waitJobFinished() {
+ if (jobId == null) {
+ return;
+ }
+ while (true) {
+ Result rep = session.getJobStatus(jobId);
+ assertOk(rep);
+ JobStatus job = rep.getValue();
+ if (job.getStatus() == JobStatus.StatusEnum.SUCCESS) {
+ logger.info("job finished");
+ break;
+ } else if (job.getStatus() == JobStatus.StatusEnum.FAILED) {
+ throw new RuntimeException("job failed");
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Test
+ @Order(4)
+ public void test3StartService() {
+ Result startServiceResponse =
+ session.startService(new StartServiceRequest().graphId(graphId));
+ if (startServiceResponse.isOk()) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println("start service success");
+ } else {
+ throw new RuntimeException(
+ "start service failed: " + startServiceResponse.getStatusMessage());
+ }
+ }
+
+ @Test
+ @Order(5)
+ public void test4CypherAdhocQuery() {
+ String query = "MATCH(a) return COUNT(a);";
+ org.neo4j.driver.Result result = neo4jSession.run(query);
+ logger.info("result: " + result.toString());
+ }
+
+ @Test
+ @Order(6)
+ public void test5GremlinAdhoQuery() throws Exception {
+ String query = "g.V().count();";
+ List results =
+ gremlinClient.submit(query).all().get();
+ logger.info("result: " + results.toString());
+ }
+
+ @Test
+ @Order(7)
+ public void test6CreateProcedure() {
+ CreateProcedureRequest procedure = new CreateProcedureRequest();
+ procedure.setName("testProcedure");
+ procedure.setDescription("a simple test procedure");
+ procedure.setQuery("MATCH(p:person) RETURN COUNT(p);");
+ procedure.setType(CreateProcedureRequest.TypeEnum.CYPHER);
+ Result resp = session.createProcedure(graphId, procedure);
+ assertOk(resp);
+ procedureId = "testProcedure";
+ }
+
+ @Test
+ @Order(8)
+ public void test7Restart() {
+ Result resp = session.restartService();
+ assertOk(resp);
+ // Sleep 5 seconds to wait for the service to restart
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ logger.info("service restarted: " + resp.getValue());
+ }
+
+ @Test
+ @Order(9)
+ public void test8CallProcedureViaNeo4j() {
+ org.neo4j.driver.Result result = neo4jSession.run("CALL testProcedure() YIELD *;");
+ logger.info("result: " + result.toString());
+ }
+
+ @AfterAll
+ public static void afterClass() {
+ logger.info("clean up");
+ if (graphId != null) {
+ if (procedureId != null) {
+ Result resp = session.deleteProcedure(graphId, procedureId);
+ logger.info("procedure deleted: " + resp.getValue());
+ }
+ Result resp = session.deleteGraph(graphId);
+ assertOk(resp);
+ logger.info("graph deleted: " + resp.getValue());
+ }
+ }
+
+ private static boolean assertOk(Result result) {
+ if (!result.isOk()) {
+ System.out.println(result.getStatusMessage());
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/flex/interactive/sdk/python/.openapi-generator-ignore b/flex/interactive/sdk/python/.openapi-generator-ignore
new file mode 100644
index 000000000000..4ae43deab14e
--- /dev/null
+++ b/flex/interactive/sdk/python/.openapi-generator-ignore
@@ -0,0 +1,32 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
+
+
+README.md
+setup.py
+interactive_sdk/__init__.py
+interactive_sdk/client
+requirements.txt
+test-requirements.txt
+test/
\ No newline at end of file
diff --git a/flex/interactive/sdk/python/README.md b/flex/interactive/sdk/python/README.md
new file mode 100644
index 000000000000..0dff6d5e7ed9
--- /dev/null
+++ b/flex/interactive/sdk/python/README.md
@@ -0,0 +1,66 @@
+# GraphScope Interactive Python SDK
+
+This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
+
+- API version: 1.0.0
+- Package version: 0.0.3
+- Build package: org.openapitools.codegen.languages.PythonClientCodegen
+
+## Requirements.
+
+Python 3.7+
+
+## Installation & Usage
+
+### pip install
+
+Currently not support, can only be built from source.
+
+### Setuptools
+
+Install via [Setuptools](http://pypi.python.org/pypi/setuptools).
+
+```sh
+python setup.py install --user
+```
+(or `sudo python setup.py install` to install the package for all users)
+
+Then import the package:
+```python
+import interactive_sdk
+```
+
+### Tests
+
+Execute `pytest` to run the tests.
+
+## Getting Started
+
+First, install and start the interactive service via [Interactive Getting Started](https://graphscope.io/docs/flex/interactive/getting_started), and you will get the endpoint for the Interactive service.
+
+```bash
+Interactive Service is listening at ${INTERACTIVE_ENDPOINT}.
+```
+
+Then, connect to the interactive endpoint, and try to run a simple query with following code.
+
+```python
+
+from interactive_sdk.client.driver import Driver
+
+# replace endpoint with the actual interactive endpoint, this is mock server just for testing.
+interactive_endpoint='https://virtserver.swaggerhub.com/GRAPHSCOPE/interactive/1.0.0/'
+driver = Driver(endpoint=interactive_endpoint)
+
+# Interactive will initially start on a builtin modern graph. You can run a simple cypher query
+with driver.getNeo4jSession() as session:
+ resp = session.run('MATCH(n) RETURN COUNT(n);')
+ for record in resp:
+ print('record: ', record)
+ # record:
+```
+
+For a more detailed example, please refer to [Python SDK Example](https://github.com/alibaba/GraphScope/flex/interactive/sdk/examples/python/basic_example.py).
+
+
+
diff --git a/flex/interactive/sdk/python/interactive_sdk/__init__.py b/flex/interactive/sdk/python/interactive_sdk/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/flex/interactive/sdk/python/interactive_sdk/client/__init__.py b/flex/interactive/sdk/python/interactive_sdk/client/__init__.py
new file mode 100644
index 000000000000..92de0f29159b
--- /dev/null
+++ b/flex/interactive/sdk/python/interactive_sdk/client/__init__.py
@@ -0,0 +1,6 @@
+# flake8: noqa
+
+from interactive_sdk.client.driver import Driver
+from interactive_sdk.client.result import Result
+from interactive_sdk.client.session import DefaultSession, Session
+from interactive_sdk.client.status import Status
diff --git a/flex/interactive/sdk/python/interactive_sdk/client/driver.py b/flex/interactive/sdk/python/interactive_sdk/client/driver.py
new file mode 100644
index 000000000000..8629decab195
--- /dev/null
+++ b/flex/interactive/sdk/python/interactive_sdk/client/driver.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
+#
+# Licensed 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.
+#
+
+import sys
+
+from gremlin_python import statics
+from gremlin_python.driver.client import Client
+from gremlin_python.driver.driver_remote_connection import \
+ DriverRemoteConnection
+from gremlin_python.process.graph_traversal import __
+from gremlin_python.process.strategies import *
+from gremlin_python.structure.graph import Graph
+from neo4j import GraphDatabase
+from neo4j import Session as Neo4jSession
+
+from interactive_sdk.client.session import DefaultSession, Session
+
+
+class Driver:
+ def __init__(self, endpoint: str):
+ # split uri into host and port
+ self._endpoint = endpoint
+ # prepend http:// to self._endpoint
+ if not self._endpoint.startswith("http://"):
+ raise ValueError("Invalid uri, expected format is http://host:port")
+ host_and_port = self._endpoint[7:]
+ splitted = host_and_port.split(":")
+ if len(splitted) != 2:
+ raise ValueError("Invalid uri, expected format is host:port")
+ self._host = splitted[0]
+ self._port = int(splitted[1])
+ self._session = None
+
+ def session(self) -> Session:
+ return DefaultSession(self._endpoint)
+
+ def getDefaultSession(self) -> Session:
+ if self._session is None:
+ self._session = self.session()
+ return self._session
+
+ def getNeo4jSession(self, **config) -> Neo4jSession:
+ return self.getNeo4jSessionImpl(**config)
+
+ def getGremlinClient(self) -> str:
+ return self.getGremlinClientImpl()
+
+ @property
+ def get_host(self) -> str:
+ return self._host
+
+ @property
+ def get_port(self) -> int:
+ return self._port
+
+ def getNeo4jSessionImpl(self, **config) -> Neo4jSession:
+ endpoint = self.getNeo4jEndpoint()
+ return GraphDatabase.driver(endpoint, auth=None).session(**config)
+
+ def getNeo4jEndpoint(self) -> str:
+ service_status = self.getDefaultSession().get_service_status()
+ if service_status.is_ok():
+ bolt_port = service_status.get_value().bolt_port
+ return "bolt://" + self._host + ":" + str(bolt_port)
+ else:
+ raise ValueError(
+ "Failed to get service status " + service_status.get_status_message()
+ )
+
+ def getGremlinClientImpl(self):
+ gremlin_endpoint = self.getGremlinEndpoint()
+ graph_url = "ws://" + gremlin_endpoint + "/gremlin"
+ return Client(graph_url, "g")
+
+ def getGremlinEndpoint(self):
+ service_status = self.getDefaultSession().get_service_status()
+ if service_status.is_ok():
+ gremlin_port = service_status.get_value().gremlin_port
+ return self._host + ":" + str(gremlin_port)
+ else:
+ raise ValueError(
+ "Failed to get service status " + service_status.get_status_message()
+ )
diff --git a/flex/interactive/sdk/python/interactive_sdk/client/result.py b/flex/interactive/sdk/python/interactive_sdk/client/result.py
new file mode 100644
index 000000000000..c623bb36f256
--- /dev/null
+++ b/flex/interactive/sdk/python/interactive_sdk/client/result.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
+#
+# Licensed 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.
+#
+
+from typing import Generic, TypeVar
+
+from pydantic import Field
+
+from interactive_sdk.openapi.api_response import ApiResponse
+from interactive_sdk.client.status import Status
+from interactive_sdk.openapi.exceptions import ApiException
+
+# Define a generic type placeholder
+T = TypeVar("T")
+
+
+# Generate a python class Result, which has two field status and value. This class can be used to wrap the execution result for interface where exception may happen
+class Result(Generic[T]):
+ def __init__(self, status: Status, value: T):
+ self.status = status
+ self.value = value
+
+ def __str__(self):
+ return f"Result: {self.status}, value: {self.value}"
+
+ def __repr__(self):
+ return f"Result: {self.status}, value: {self.value}"
+
+ def is_ok(self):
+ return self.status.is_ok()
+
+ def is_error(self):
+ return self.status.is_error()
+
+ def get_value(self):
+ return self.value
+
+ def get_status(self):
+ return self.status
+
+ def get_status_message(self):
+ return self.status.message
+
+ @staticmethod
+ def ok(value):
+ return Result(Status.ok(), value)
+
+ @staticmethod
+ def error(status: Status, msg: str):
+ return Result(status, msg)
+
+ @staticmethod
+ def from_exception(exception: ApiException):
+ return Result(Status.from_exception(exception), None)
+
+ @staticmethod
+ def from_response(response: ApiResponse):
+ return Result(Status.from_response(response), response.data)
diff --git a/flex/interactive/sdk/python/interactive_sdk/client/session.py b/flex/interactive/sdk/python/interactive_sdk/client/session.py
new file mode 100644
index 000000000000..6015a28c386f
--- /dev/null
+++ b/flex/interactive/sdk/python/interactive_sdk/client/session.py
@@ -0,0 +1,540 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
+#
+# Licensed 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.
+#
+
+from abc import ABCMeta, abstractmethod
+from typing import Annotated, Any, Dict, List, Optional, Union
+
+from pydantic import Field, StrictStr
+
+from interactive_sdk.openapi.api.admin_service_graph_management_api import (
+ AdminServiceGraphManagementApi,
+)
+from interactive_sdk.openapi.api.admin_service_job_management_api import (
+ AdminServiceJobManagementApi,
+)
+from interactive_sdk.openapi.api.admin_service_procedure_management_api import (
+ AdminServiceProcedureManagementApi,
+)
+from interactive_sdk.openapi.api.admin_service_service_management_api import (
+ AdminServiceServiceManagementApi,
+)
+from interactive_sdk.openapi.api.graph_service_edge_management_api import (
+ GraphServiceEdgeManagementApi,
+)
+from interactive_sdk.openapi.api.graph_service_vertex_management_api import (
+ GraphServiceVertexManagementApi,
+)
+from interactive_sdk.openapi.api.query_service_api import QueryServiceApi
+from interactive_sdk.openapi.api_client import ApiClient
+from interactive_sdk.client.result import Result
+from interactive_sdk.openapi.configuration import Configuration
+from interactive_sdk.openapi.models.create_graph_request import CreateGraphRequest
+from interactive_sdk.openapi.models.create_graph_response import CreateGraphResponse
+from interactive_sdk.openapi.models.create_procedure_request import (
+ CreateProcedureRequest,
+)
+from interactive_sdk.openapi.models.create_procedure_response import (
+ CreateProcedureResponse,
+)
+from interactive_sdk.openapi.models.edge_request import EdgeRequest
+from interactive_sdk.openapi.models.get_graph_response import GetGraphResponse
+from interactive_sdk.openapi.models.get_graph_schema_response import (
+ GetGraphSchemaResponse,
+)
+from interactive_sdk.openapi.models.get_procedure_response import GetProcedureResponse
+from interactive_sdk.openapi.models.job_response import JobResponse
+from interactive_sdk.openapi.models.job_status import JobStatus
+from interactive_sdk.openapi.models.schema_mapping import SchemaMapping
+from interactive_sdk.openapi.models.service_status import ServiceStatus
+from interactive_sdk.openapi.models.start_service_request import StartServiceRequest
+from interactive_sdk.openapi.models.update_procedure_request import (
+ UpdateProcedureRequest,
+)
+from interactive_sdk.openapi.models.vertex_request import VertexRequest
+
+
+class EdgeInterface(metaclass=ABCMeta):
+ @abstractmethod
+ def add_edge(self, graph_id: StrictStr, edge_request: EdgeRequest) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def delete_edge(
+ self,
+ graph_id: StrictStr,
+ src_label: Annotated[
+ StrictStr, Field(description="The label name of src vertex.")
+ ],
+ src_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of src vertex.")
+ ],
+ dst_label: Annotated[
+ StrictStr, Field(description="The label name of dst vertex.")
+ ],
+ dst_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of dst vertex.")
+ ],
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def get_edge(
+ self,
+ graph_id: StrictStr,
+ src_label: Annotated[
+ StrictStr, Field(description="The label name of src vertex.")
+ ],
+ src_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of src vertex.")
+ ],
+ dst_label: Annotated[
+ StrictStr, Field(description="The label name of dst vertex.")
+ ],
+ dst_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of dst vertex.")
+ ],
+ ) -> Result[Union[None, EdgeRequest]]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def update_edge(
+ self, graph_id: StrictStr, edge_request: EdgeRequest
+ ) -> Result[str]:
+ raise NotImplementedError
+
+
+class VertexInterface(metaclass=ABCMeta):
+ @abstractmethod
+ def add_vertex(
+ self, graph_id: StrictStr, vertex_request: VertexRequest
+ ) -> Result[StrictStr]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def delete_vertex(
+ self,
+ graph_id: StrictStr,
+ label: Annotated[StrictStr, Field(description="The label name of vertex.")],
+ primary_key_value: Annotated[
+ Any, Field(description="The primary key value of vertex.")
+ ],
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def get_vertex(
+ self,
+ graph_id: StrictStr,
+ label: Annotated[StrictStr, Field(description="The label name of vertex.")],
+ primary_key_value: Annotated[
+ Any, Field(description="The primary key value of vertex.")
+ ],
+ ) -> Result[VertexRequest]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def update_vertex(
+ self, graph_id: StrictStr, vertex_request: VertexRequest
+ ) -> Result[str]:
+ raise NotImplementedError
+
+
+class GraphInterface(metaclass=ABCMeta):
+ @abstractmethod
+ def create_graph(self, graph: CreateGraphRequest) -> Result[CreateGraphResponse]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def get_schema(
+ graph_id: Annotated[
+ StrictStr, Field(description="The name of graph to delete")
+ ],
+ ) -> Result[GetGraphSchemaResponse]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def delete_graph(
+ graph_id: Annotated[
+ StrictStr, Field(description="The name of graph to delete")
+ ],
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def list_graphs(self) -> Result[List[GetGraphResponse]]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def bulk_loading(
+ self,
+ graph_id: Annotated[StrictStr, Field(description="The id of graph to load")],
+ schema_mapping: SchemaMapping,
+ ) -> Result[JobResponse]:
+ raise NotImplementedError
+
+
+class ProcedureInterface(metaclass=ABCMeta):
+ @abstractmethod
+ def create_procedure(
+ self, graph_id: StrictStr, procedure: CreateProcedureRequest
+ ) -> Result[CreateProcedureResponse]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def delete_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def list_procedures(
+ self, graph_id: StrictStr
+ ) -> Result[List[GetProcedureResponse]]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def update_procedure(
+ self, graph_id: StrictStr, procedure: UpdateProcedureRequest
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def get_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr
+ ) -> Result[GetProcedureResponse]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def call_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr, params: Dict[str, Any]
+ ) -> Result[str]:
+ raise NotImplementedError
+
+
+class QueryServiceInterface:
+ @abstractmethod
+ def get_service_status(self) -> Result[ServiceStatus]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def start_service(
+ self,
+ start_service_request: Annotated[
+ Optional[StartServiceRequest],
+ Field(description="Start service on a specified graph"),
+ ] = None,
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def stop_service(self) -> Result[str]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def restart_service(self) -> Result[str]:
+ raise NotImplementedError
+
+
+class JobInterface(metaclass=ABCMeta):
+ @abstractmethod
+ def get_job(self, job_id: StrictStr) -> Result[JobStatus]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def list_jobs(self) -> Result[List[JobResponse]]:
+ raise NotImplementedError
+
+ @abstractmethod
+ def cancel_job(self, job_id: StrictStr) -> Result[str]:
+ raise NotImplementedError
+
+
+class Session(
+ VertexInterface,
+ EdgeInterface,
+ GraphInterface,
+ ProcedureInterface,
+ JobInterface,
+ QueryServiceInterface,
+):
+ pass
+
+
+class DefaultSession(Session):
+ def __init__(self, uri: str):
+ self._client = ApiClient(Configuration(host=uri))
+
+ self._graph_api = AdminServiceGraphManagementApi(self._client)
+ self._job_api = AdminServiceJobManagementApi(self._client)
+ self._procedure_api = AdminServiceProcedureManagementApi(self._client)
+ self._service_api = AdminServiceServiceManagementApi(self._client)
+ self._edge_api = GraphServiceEdgeManagementApi(self._client)
+ self._vertex_api = GraphServiceVertexManagementApi(self._client)
+ self._query_api = QueryServiceApi(self._client)
+
+ def __enter__(self):
+ self._client.__enter__()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self._client.__exit__(exc_type=exc_type, exc_value=exc_val, traceback=exc_tb)
+
+ # implementations of the methods from the interfaces
+ ################ Vertex Interfaces ##########
+ def add_vertex(
+ self, graph_id: StrictStr, vertex_request: VertexRequest
+ ) -> Result[StrictStr]:
+ raise NotImplementedError
+
+ def delete_vertex(
+ self,
+ graph_id: StrictStr,
+ label: Annotated[StrictStr, Field(description="The label name of vertex.")],
+ primary_key_value: Annotated[
+ Any, Field(description="The primary key value of vertex.")
+ ],
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ def get_vertex(
+ self,
+ graph_id: StrictStr,
+ label: Annotated[StrictStr, Field(description="The label name of vertex.")],
+ primary_key_value: Annotated[
+ Any, Field(description="The primary key value of vertex.")
+ ],
+ ) -> Result[VertexRequest]:
+ raise NotImplementedError
+
+ def update_vertex(
+ self, graph_id: StrictStr, vertex_request: VertexRequest
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ ################ Edge Interfaces ##########
+ def add_edge(self, graph_id: StrictStr, edge_request: EdgeRequest) -> Result[str]:
+ raise NotImplementedError
+
+ def delete_edge(
+ self,
+ graph_id: StrictStr,
+ src_label: Annotated[
+ StrictStr, Field(description="The label name of src vertex.")
+ ],
+ src_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of src vertex.")
+ ],
+ dst_label: Annotated[
+ StrictStr, Field(description="The label name of dst vertex.")
+ ],
+ dst_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of dst vertex.")
+ ],
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ def get_edge(
+ self,
+ graph_id: StrictStr,
+ src_label: Annotated[
+ StrictStr, Field(description="The label name of src vertex.")
+ ],
+ src_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of src vertex.")
+ ],
+ dst_label: Annotated[
+ StrictStr, Field(description="The label name of dst vertex.")
+ ],
+ dst_primary_key_value: Annotated[
+ Any, Field(description="The primary key value of dst vertex.")
+ ],
+ ) -> Result[Union[None, EdgeRequest]]:
+ raise NotImplementedError
+
+ def update_edge(
+ self, graph_id: StrictStr, edge_request: EdgeRequest
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ ################ Graph Interfaces ##########
+ def create_graph(self, graph: CreateGraphRequest) -> Result[CreateGraphResponse]:
+ try:
+ response = self._graph_api.create_graph_with_http_info(graph)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def get_schema(
+ self,
+ graph_id: Annotated[StrictStr, Field(description="The name of graph to get")],
+ ) -> Result[GetGraphSchemaResponse]:
+ try:
+ response = self._graph_api.get_schema_with_http_info(graph_id)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def delete_graph(
+ self,
+ graph_id: Annotated[
+ StrictStr, Field(description="The name of graph to delete")
+ ],
+ ) -> Result[str]:
+ try:
+ response = self._graph_api.delete_graph_with_http_info(graph_id)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def list_graphs(self) -> Result[List[GetGraphResponse]]:
+ try:
+ response = self._graph_api.list_graphs_with_http_info()
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def bulk_loading(
+ self,
+ graph_id: Annotated[StrictStr, Field(description="The id of graph to load")],
+ schema_mapping: SchemaMapping,
+ ) -> Result[JobResponse]:
+ try:
+ response = self._graph_api.create_dataloading_job_with_http_info(
+ graph_id, schema_mapping
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ ################ Procedure Interfaces ##########
+ def create_procedure(
+ self, graph_id: StrictStr, procedure: CreateProcedureRequest
+ ) -> Result[CreateProcedureResponse]:
+ try:
+ response = self._procedure_api.create_procedure_with_http_info(
+ graph_id, procedure
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def delete_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr
+ ) -> Result[str]:
+ try:
+ response = self._procedure_api.delete_procedure_with_http_info(
+ graph_id, procedure_id
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def list_procedures(
+ self, graph_id: StrictStr
+ ) -> Result[List[GetProcedureResponse]]:
+ try:
+ response = self._procedure_api.list_procedures_with_http_info(graph_id)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def update_procedure(
+ self, graph_id: StrictStr, procedure: UpdateProcedureRequest
+ ) -> Result[str]:
+ try:
+ response = self._procedure_api.update_procedure_with_http_info(
+ graph_id, procedure
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def get_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr
+ ) -> Result[GetProcedureResponse]:
+ try:
+ response = self._procedure_api.get_procedure_with_http_info(
+ graph_id, procedure_id
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def call_procedure(
+ self, graph_id: StrictStr, procedure_id: StrictStr, params: Dict[str, Any]
+ ) -> Result[str]:
+ raise NotImplementedError
+
+ ################ QueryService Interfaces ##########
+ def get_service_status(self) -> Result[ServiceStatus]:
+ try:
+ response = self._service_api.get_service_status_with_http_info()
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def start_service(
+ self,
+ start_service_request: Annotated[
+ Optional[StartServiceRequest],
+ Field(description="Start service on a specified graph"),
+ ] = None,
+ ) -> Result[str]:
+ try:
+ response = self._service_api.start_service_with_http_info(
+ start_service_request
+ )
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def stop_service(self) -> Result[str]:
+ try:
+ response = self._service_api.stop_service_with_http_info()
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def restart_service(self) -> Result[str]:
+ try:
+ response = self._service_api.restart_service_with_http_info()
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ ################ Job Interfaces ##########
+ def get_job(self, job_id: StrictStr) -> Result[JobStatus]:
+ try:
+ response = self._job_api.get_job_by_id_with_http_info(job_id)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def list_jobs(self) -> Result[List[JobResponse]]:
+ try:
+ response = self._job_api.list_jobs_with_http_info()
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
+
+ def cancel_job(self, job_id: StrictStr) -> Result[str]:
+ try:
+ response = self._job_api.delete_job_by_id_with_http_info(job_id)
+ return Result.from_response(response)
+ except Exception as e:
+ return Result.from_exception(e)
diff --git a/flex/interactive/sdk/python/interactive_sdk/client/status.py b/flex/interactive/sdk/python/interactive_sdk/client/status.py
new file mode 100644
index 000000000000..b4542223651e
--- /dev/null
+++ b/flex/interactive/sdk/python/interactive_sdk/client/status.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
+#
+# Licensed 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.
+#
+
+
+from enum import Enum
+
+from interactive_sdk.openapi.api_response import ApiResponse
+from interactive_sdk.openapi.exceptions import (
+ ApiException,
+ BadRequestException,
+ ForbiddenException,
+ NotFoundException,
+ ServiceException,
+ UnauthorizedException,
+)
+
+
+class StatusCode(Enum):
+ OK = 0
+ BAD_REQUEST = 1
+ FORBIDDEN = 2
+ NOT_FOUND = 3
+ SERVER_INTERNAL_ERROR = 4
+ SERVICE_UNAVAILABLE = 5
+ UNKNOWN = 6
+
+
+class Status:
+ def __init__(self, status: StatusCode, message: str):
+ self.status = status
+ self.message = message
+
+ def __str__(self):
+ return f"Status: {self.status}, message: {self.message}"
+
+ def __repr__(self):
+ return f"Status: {self.status}, message: {self.message}"
+
+ def is_ok(self) -> bool:
+ return self.status == StatusCode.OK
+
+ def is_error(self) -> bool:
+ return self.status != StatusCode.OK
+
+ @property
+ def get_message(self):
+ return self.message
+
+ # static method create a server internal error object
+ @staticmethod
+ def server_internal_error(message: str):
+ return Status(StatusCode.SERVER_INTERNAL_ERROR, message)
+
+ @staticmethod
+ def from_exception(exception: ApiException):
+ # mapping from ApiException to StatusCode
+ if isinstance(exception, BadRequestException):
+ return Status(StatusCode.BAD_REQUEST, exception.reason)
+ elif isinstance(exception, ForbiddenException):
+ return Status(StatusCode.FORBIDDEN, exception.reason)
+ elif isinstance(exception, NotFoundException):
+ return Status(StatusCode.NOT_FOUND, exception.reason)
+ elif isinstance(exception, UnauthorizedException):
+ return Status(StatusCode.BAD_REQUEST, exception.reason)
+ elif isinstance(exception, ServiceException):
+ return Status(StatusCode.SERVER_INTERNAL_ERROR, exception.reason)
+ return Status(
+ StatusCode.UNKNOWN, "Unknown Error from exception " + str(exception)
+ )
+
+ @staticmethod
+ def from_response(response: ApiResponse):
+ # mapping from ApiResponse to StatusCode
+ if response.status_code == 200:
+ return Status(StatusCode.OK, "OK")
+ if response.status_code == 400:
+ return Status(StatusCode.BAD_REQUEST, "Bad Request")
+ if response.status_code == 403:
+ return Status(StatusCode.FORBIDDEN, "Forbidden")
+ if response.status_code == 404:
+ return Status(StatusCode.NOT_FOUND, "Not Found")
+ if response.status_code == 401:
+ return Status(StatusCode.BAD_REQUEST, "Unauthorized")
+ if response.status_code == 500:
+ return Status(StatusCode.SERVER_INTERNAL_ERROR, "Internal Server Error")
+ if response.status_code == 503:
+ return Status(StatusCode.SERVICE_UNAVAILABLE, "Service Unavailable")
+ return Status(StatusCode.UNKNOWN, "Unknown Error")
+
+ @staticmethod
+ def ok():
+ return Status(StatusCode.OK, "OK")
diff --git a/flex/interactive/sdk/python/requirements.txt b/flex/interactive/sdk/python/requirements.txt
new file mode 100644
index 000000000000..2f9ac9891532
--- /dev/null
+++ b/flex/interactive/sdk/python/requirements.txt
@@ -0,0 +1,7 @@
+python_dateutil >= 2.5.3
+setuptools >= 21.0.0
+urllib3 >= 1.25.3, < 2.1.0
+pydantic >= 2
+typing-extensions >= 4.7.1
+neo4j >= 4.4.19
+gremlinpython >= 3.4.10
diff --git a/flex/interactive/sdk/python/setup.cfg b/flex/interactive/sdk/python/setup.cfg
new file mode 100644
index 000000000000..11433ee875ab
--- /dev/null
+++ b/flex/interactive/sdk/python/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length=99
diff --git a/flex/interactive/sdk/python/setup.py b/flex/interactive/sdk/python/setup.py
new file mode 100644
index 000000000000..26e448bbc492
--- /dev/null
+++ b/flex/interactive/sdk/python/setup.py
@@ -0,0 +1,53 @@
+# coding: utf-8
+
+"""
+ GraphScope Interactive API v0.0.3
+
+ This is the definition of GraphScope Interactive API, including - AdminService API - Vertex/Edge API - QueryService AdminService API (with tag AdminService) defines the API for GraphManagement, ProcedureManagement and Service Management. Vertex/Edge API (with tag GraphService) defines the API for Vertex/Edge management, including creation/updating/delete/retrive. QueryService API (with tag QueryService) defines the API for procedure_call, Ahodc query.
+
+ The version of the OpenAPI document: 1.0.0
+ Contact: graphscope@alibaba-inc.com
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
+
+ Do not edit the class manually.
+""" # noqa: E501
+
+
+from setuptools import find_packages, setup # noqa: H301
+
+# To install the library, run the following
+#
+# python setup.py install
+#
+# prerequisite: setuptools
+# http://pypi.python.org/pypi/setuptools
+NAME = "interactive_sdk"
+VERSION = "0.0.3"
+PYTHON_REQUIRES = ">=3.7"
+REQUIRES = [
+ "urllib3 >= 1.25.3, < 2.1.0",
+ "python-dateutil",
+ "pydantic >= 2",
+ "typing-extensions >= 4.7.1",
+ "neo4j >= 4.4.19",
+ "gremlinpython >= 3.4.10",
+]
+
+setup(
+ name=NAME,
+ version=VERSION,
+ description="GraphScope Interactive API v0.0.3",
+ author="OpenAPI Generator community",
+ author_email="graphscope@alibaba-inc.com",
+ url="",
+ keywords=["OpenAPI", "OpenAPI-Generator", "GraphScope Interactive API v0.0.3"],
+ install_requires=REQUIRES,
+ packages=find_packages(exclude=["test", "tests"]),
+ include_package_data=True,
+ license="Apache 2.0",
+ long_description_content_type="text/markdown",
+ long_description="""\
+ This is the definition of GraphScope Interactive API, including - AdminService API - Vertex/Edge API - QueryService AdminService API (with tag AdminService) defines the API for GraphManagement, ProcedureManagement and Service Management. Vertex/Edge API (with tag GraphService) defines the API for Vertex/Edge management, including creation/updating/delete/retrive. QueryService API (with tag QueryService) defines the API for procedure_call, Ahodc query.
+ """, # noqa: E501
+ package_data={"interactive_sdk": ["py.typed"]},
+)
diff --git a/flex/interactive/sdk/python/test-requirements.txt b/flex/interactive/sdk/python/test-requirements.txt
new file mode 100644
index 000000000000..3a0d0b939a1e
--- /dev/null
+++ b/flex/interactive/sdk/python/test-requirements.txt
@@ -0,0 +1,3 @@
+pytest~=7.1.3
+pytest-cov>=2.8.1
+pytest-randomly>=3.12.0
diff --git a/flex/interactive/sdk/python/test/test_driver.py b/flex/interactive/sdk/python/test/test_driver.py
new file mode 100644
index 000000000000..95efbaf6d2bc
--- /dev/null
+++ b/flex/interactive/sdk/python/test/test_driver.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
+#
+# Licensed 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.
+#
+
+
+import os
+import time
+import unittest
+
+import pytest
+
+from interactive_sdk.client.driver import Driver
+from interactive_sdk.openapi.models.base_edge_type_vertex_type_pair_relations_inner import (
+ BaseEdgeTypeVertexTypePairRelationsInner,
+)
+from interactive_sdk.openapi.models.create_edge_type import CreateEdgeType
+from interactive_sdk.openapi.models.create_graph_request import CreateGraphRequest
+from interactive_sdk.openapi.models.create_graph_schema_request import (
+ CreateGraphSchemaRequest,
+)
+from interactive_sdk.openapi.models.create_procedure_request import (
+ CreateProcedureRequest,
+)
+from interactive_sdk.openapi.models.create_property_meta import CreatePropertyMeta
+from interactive_sdk.openapi.models.create_vertex_type import CreateVertexType
+from interactive_sdk.openapi.models.edge_mapping import EdgeMapping
+from interactive_sdk.openapi.models.edge_mapping_type_triplet import (
+ EdgeMappingTypeTriplet,
+)
+from interactive_sdk.openapi.models.gs_data_type import GSDataType
+from interactive_sdk.openapi.models.job_status import JobStatus
+from interactive_sdk.openapi.models.long_text import LongText
+from interactive_sdk.openapi.models.primitive_type import PrimitiveType
+from interactive_sdk.openapi.models.schema_mapping import SchemaMapping
+from interactive_sdk.openapi.models.schema_mapping_loading_config import (
+ SchemaMappingLoadingConfig,
+)
+from interactive_sdk.openapi.models.schema_mapping_loading_config_format import (
+ SchemaMappingLoadingConfigFormat,
+)
+from interactive_sdk.openapi.models.start_service_request import StartServiceRequest
+from interactive_sdk.openapi.models.string_type import StringType
+from interactive_sdk.openapi.models.string_type_string import StringTypeString
+from interactive_sdk.openapi.models.vertex_mapping import VertexMapping
+
+
+class TestDriver(unittest.TestCase):
+ """Test usage of driver"""
+
+ def setUp(self):
+ # get endpoint from environment variable INTERACTIVE_ENDPOINT
+ self._endpoint = os.getenv("INTERACTIVE_ENDPOINT")
+ if self._endpoint is None:
+ self._endpoint = "http://localhost:7777"
+ print("endpoint: ", self._endpoint)
+ self._driver = Driver(self._endpoint)
+ self._sess = self._driver.getDefaultSession()
+ self._gremlin_client = self._driver.getGremlinClient()
+ self._graph_id = None
+ self._procedure_name = None
+ print("finish setup")
+
+ def tearDown(self):
+ if self._graph_id is not None:
+ if self._procedure_name is not None:
+ print("delete procedure: ")
+ rep1 = self._sess.delete_procedure(self._graph_id, self._procedure_name)
+ print("delete procedure: ", rep1)
+ print("delete graph: ", self._graph_id)
+ rep2 = self._sess.delete_graph(self._graph_id)
+ print("delete graph: ", rep2)
+
+ def test_example(self):
+ self.createGraph()
+ self.bulkLoading()
+ self.waitJobFinish()
+ self.runCypherQuery()
+ self.runGremlinQuery()
+ self.createProcedure()
+ self.restart()
+ self.callProcedure()
+
+ def createGraph(self):
+ create_graph = CreateGraphRequest(name="test_graph", description="test graph")
+ create_schema = CreateGraphSchemaRequest()
+ create_person_vertex = CreateVertexType(
+ type_name="person",
+ primary_keys=["id"],
+ properties=[
+ CreatePropertyMeta(
+ property_name="id",
+ property_type=GSDataType(
+ PrimitiveType(primitive_type="DT_SIGNED_INT64")
+ ),
+ ),
+ CreatePropertyMeta(
+ property_name="name",
+ property_type=GSDataType(
+ StringType(string=StringTypeString(LongText(long_text="")))
+ ),
+ ),
+ CreatePropertyMeta(
+ property_name="age",
+ property_type=GSDataType(
+ PrimitiveType(primitive_type="DT_SIGNED_INT32")
+ ),
+ ),
+ ],
+ )
+ create_schema.vertex_types = [create_person_vertex]
+ create_knows_edge = CreateEdgeType(
+ type_name="knows",
+ properties=[
+ CreatePropertyMeta(
+ property_name="weight",
+ property_type=GSDataType(PrimitiveType(primitive_type="DT_DOUBLE")),
+ )
+ ],
+ vertex_type_pair_relations=[
+ BaseEdgeTypeVertexTypePairRelationsInner(
+ source_vertex="person", destination_vertex="person"
+ )
+ ],
+ )
+ create_schema.edge_types = [create_knows_edge]
+ create_graph.var_schema = create_schema
+ resp = self._sess.create_graph(create_graph)
+ assert resp.is_ok()
+ self._graph_id = resp.get_value().graph_id
+ print("create graph: ", self._graph_id)
+
+ def bulkLoading(self):
+ assert os.environ.get("FLEX_DATA_DIR") is not None
+ person_csv_path = os.path.join(os.environ.get("FLEX_DATA_DIR"), "person.csv")
+ knows_csv_path = os.path.join(
+ os.environ.get("FLEX_DATA_DIR"), "person_knows_person.csv"
+ )
+ print("test bulk loading: ", self._graph_id)
+ schema_mapping = SchemaMapping(
+ graph=self._graph_id,
+ loading_config=SchemaMappingLoadingConfig(
+ import_option="init",
+ format=SchemaMappingLoadingConfigFormat(type="csv"),
+ ),
+ vertex_mappings=[
+ VertexMapping(type_name="person", inputs=[person_csv_path])
+ ],
+ edge_mappings=[
+ EdgeMapping(
+ type_triplet=EdgeMappingTypeTriplet(
+ edge="knows",
+ source_vertex="person",
+ destination_vertex="person",
+ ),
+ inputs=[knows_csv_path],
+ )
+ ],
+ )
+ resp = self._sess.bulk_loading(self._graph_id, schema_mapping)
+ assert resp.is_ok()
+ self._job_id = resp.get_value().job_id
+
+ def waitJobFinish(self):
+ assert self._job_id is not None
+ while True:
+ resp = self._sess.get_job(self._job_id)
+ assert resp.is_ok()
+ status = resp.get_value().status
+ print("job status: ", status)
+ if status == "SUCCESS":
+ break
+ elif status == "FAILED":
+ raise Exception("job failed")
+ else:
+ time.sleep(1)
+ print("job finished")
+
+ def runCypherQuery(self):
+ query = "MATCH (n) RETURN COUNT(n);"
+ with self._driver.getNeo4jSession() as session:
+ resp = session.run(query)
+ print("cypher query result: ", resp)
+
+ def runGremlinQuery(self):
+ query = "g.V().count();"
+ ret = []
+ q = self._gremlin_client.submit(query)
+ while True:
+ try:
+ ret.extend(q.next())
+ except StopIteration:
+ break
+ print(ret)
+
+ def createProcedure(self):
+ self._procedure_name = "test_procedure"
+ create_proc_request = CreateProcedureRequest(
+ name=self._procedure_name,
+ description="test procedure",
+ query="MATCH (n) RETURN COUNT(n);",
+ type="cypher",
+ )
+ resp = self._sess.create_procedure(self._graph_id, create_proc_request)
+ assert resp.is_ok()
+ print("create procedure: ", resp.get_value())
+
+ def restart(self):
+ resp = self._sess.start_service(
+ start_service_request=StartServiceRequest(graph_id=self._graph_id)
+ )
+ assert resp.is_ok()
+ print("restart: ", resp.get_value())
+ # wait 5 seconds
+ time.sleep(5)
+
+ def callProcedure(self):
+ with self._driver.getNeo4jSession() as session:
+ result = session.run("CALL test_procedure();")
+ print("call procedure result: ", result)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/flex/openapi/openapi_interactive.yaml b/flex/openapi/openapi_interactive.yaml
index c41f1caef0eb..81dac7968eee 100644
--- a/flex/openapi/openapi_interactive.yaml
+++ b/flex/openapi/openapi_interactive.yaml
@@ -1,41 +1,59 @@
openapi: 3.0.3
+servers:
+ # Added by API Auto Mocking Plugin
+ # Added by API Auto Mocking Plugin
+ - description: SwaggerHub API Auto Mocking
+ url: https://virtserver.swaggerhub.com/GRAPHSCOPE/InteractiveAPI/1.0.0
+ - description: SwaggerHub API Auto Mocking
+ url: https://virtserver.swaggerhub.com/GRAPHSCOPE/interactive/1.0.0
info:
- title: GraphScope Interactive API
- description: |-
- This is a specification for GraphScope Interactive based on the OpenAPI 3.0 specification. You can find out more details about
- specification at [doc](https://swagger.io/specification/v3/).
+ description: |
+ This is the definition of GraphScope Interactive API, including
+ - AdminService API
+ - Vertex/Edge API
+ - QueryService
- Some useful links:
- - [GraphScope Repository](https://github.com/alibaba/GraphScope)
- - [The Source API definition for GraphScope Interactive](#)
+
+ AdminService API (with tag AdminService) defines the API for GraphManagement, ProcedureManagement and Service Management.
+
+ Vertex/Edge API (with tag GraphService) defines the API for Vertex/Edge management, including creation/updating/delete/retrive.
+
+ QueryService API (with tag QueryService) defines the API for procedure_call, Ahodc query.
+ version: "1.0.0"
+ title: GraphScope Interactive API v0.0.3
contact:
- name: GraphScope
email: graphscope@alibaba-inc.com
license:
name: Apache 2.0
- url: http://www.apache.org/licenses/LICENSE-2.0.html
- version: 0.9.1
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
externalDocs:
description: Find out More about GraphScope
url: http://graphscope.io
tags:
- - name: graph
- description: Everything about interactive graph
- externalDocs:
- description: Find out more
- url: http://graphscope.io
+ - name: AdminService/GraphManagement
+ description: GraphManagement
+ - name: AdminService/ProcedureManagement
+ description: ProcedureManagement
+ - name: AdminService/ServiceManagement
+ description: ServiceManagement
+ - name: GraphService/VertexManagement
+ description: VertexManagement
+ - name: GraphService/EdgeManagement
+ description: EdgeManagement
+ - name: QueryService
+ description: Graph query
paths:
/v1/graph:
post:
tags:
- - graph
+ - AdminService/GraphManagement
description: Create a new graph
operationId: create_graph
requestBody:
content:
application/json:
schema:
- $ref: '#/components/schemas/Graph'
+ $ref: '#/components/schemas/CreateGraphRequest'
required: true
responses:
'200':
@@ -43,10 +61,22 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/CreateGraphResponse'
+ '400':
+ description: BadRequest
+ content:
+ application/json:
+ schema:
+ type: string
+ '500':
+ description: Internal error
+ content:
+ application/json:
+ schema:
+ type: string
get:
tags:
- - graph
+ - AdminService/GraphManagement
description: List all graphs
operationId: list_graphs
responses:
@@ -57,57 +87,103 @@ paths:
schema:
type: array
items:
- $ref: '#/components/schemas/Graph'
- /v1/graph/{graph_name}:
+ $ref: '#/components/schemas/GetGraphResponse'
+ /v1/graph/{graph_id}:
+ get:
+ tags:
+ - AdminService/GraphManagement
+ description: Get a graph by name
+ operationId: get_graph
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ description: The name of graph to get
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GetGraphResponse'
+ '404':
+ description: Not found
+ content:
+ application/json:
+ schema:
+ type: string
+ example: graph not exists
delete:
tags:
- - graph
+ - AdminService/GraphManagement
description: Delete a graph by name
operationId: delete_graph
parameters:
- - name: graph_name
+ - name: graph_id
in: path
required: true
schema:
type: string
+ description:
+ The name of graph to delete
responses:
'200':
description: Successful operation
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
- /v1/graph/{graph_name}/schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Successfully delete graph"
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Graph not found"
+ '500':
+ description: Internal Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Internal Error: "
+ /v1/graph/{graph_id}/schema:
get:
tags:
- - graph
+ - AdminService/GraphManagement
description: Get schema by graph name
operationId: get_schema
parameters:
- - name: graph_name
- in : path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ description: The name of graph to delete
responses:
'200':
description: successful operation
content:
application/json:
schema:
- $ref: '#/components/schemas/Schema'
- /v1/graph/{graph_name}/dataloading:
+ $ref: '#/components/schemas/GetGraphSchemaResponse'
+
+ /v1/graph/{graph_id}/dataloading:
post:
tags:
- - job
+ - AdminService/GraphManagement
description: Create a dataloading job
operationId: create_dataloading_job
parameters:
- - name: graph_name
- in: path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in: path
+ required: true
+ schema:
+ type: string
+ description: The name of graph to do bulk loading.
requestBody:
content:
application/json:
@@ -124,7 +200,7 @@ paths:
/v1/job/{job_id}:
get:
tags:
- - job
+ - AdminService/JobManagement
operationId: get_job_by_id
parameters:
- name: job_id
@@ -132,6 +208,7 @@ paths:
required: true
schema:
type: string
+ description: The id of the job, returned from POST /v1/graph/{graph_id}/dataloading
responses:
'200':
description: successful operation
@@ -141,7 +218,7 @@ paths:
$ref: '#/components/schemas/JobStatus'
delete:
tags:
- - job
+ - AdminService/JobManagement
operationId: delete_job_by_id
parameters:
- name: job_id
@@ -155,11 +232,12 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/APIResponse'
+ example: "Successfully cancel job: 123"
/v1/job:
get:
tags:
- - job
+ - AdminService/JobManagement
operationId: list_jobs
responses:
'200':
@@ -170,23 +248,23 @@ paths:
type: array
items:
$ref: '#/components/schemas/JobStatus'
- /v1/graph/{graph_name}/procedure:
+ /v1/graph/{graph_id}/procedure:
post:
tags:
- - procedure
+ - AdminService/ProcedureManagement
description: Create a new procedure on a graph
operationId: create_procedure
parameters:
- - name: graph_name
- in: path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in: path
+ required: true
+ schema:
+ type: string
requestBody:
content:
application/json:
schema:
- $ref: '#/components/schemas/Procedure'
+ $ref: '#/components/schemas/CreateProcedureRequest'
required: true
responses:
'200':
@@ -194,18 +272,39 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/CreateProcedureResponse'
+ '400':
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Bad request"
+ '404':
+ description: not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Graph not found"
+ '500':
+ description: Internal Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example: "Internal error"
get:
tags:
- - procedure
+ - AdminService/ProcedureManagement
description: List all procedures
operationId: list_procedures
parameters:
- - name: graph_name
- in: path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in: path
+ required: true
+ schema:
+ type: string
responses:
'200':
description: Successful operation
@@ -214,71 +313,106 @@ paths:
schema:
type: array
items:
- $ref: '#/components/schemas/Procedure'
- /v1/graph/{graph_name}/procedure/{procedure_name}:
+ $ref: '#/components/schemas/GetProcedureResponse'
+ '404':
+ description: Not found
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "Graph not found"
+ /v1/graph/{graph_id}/procedure/{procedure_id}:
get:
tags:
- - procedure
+ - AdminService/ProcedureManagement
description: Get a procedure by name
operationId: get_procedure
parameters:
- - name: graph_name
- in : path
- required: true
- schema:
- type: string
- - name: procedure_name
- in : path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ - name: procedure_id
+ in : path
+ required: true
+ schema:
+ type: string
responses:
'200':
description: successful operation
content:
application/json:
schema:
- $ref: '#/components/schemas/Procedure'
+ $ref: '#/components/schemas/GetProcedureResponse'
+ '404':
+ description: Not found
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "Graph not found/procedure not found"
put:
tags:
- - procedure
+ - AdminService/ProcedureManagement
description: Update procedure on a graph by name
operationId: update_procedure
parameters:
- - name: graph_name
- in: path
- required: true
- schema:
- type: string
- - name: procedure_name
- in : path
- required: true
- schema:
- type: string
+ - name: graph_id
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: procedure_id
+ in : path
+ required: true
+ schema:
+ type: string
requestBody:
content:
application/json:
schema:
- $ref: '#/components/schemas/Procedure'
+ $ref: '#/components/schemas/UpdateProcedureRequest'
responses:
'200':
- description: Successful operation
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ApiResponse'
+ description: Successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ '400':
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "bad request"
+ "404":
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "Graph Not found/Procedure Not found"
+ "500":
+ description: Internal error
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "bad request"
delete:
tags:
- - procedure
+ - AdminService/ProcedureManagement
description: Delete a procedure on a graph by name
operationId: delete_procedure
parameters:
- - name: graph_name
+ - name: graph_id
in: path
required: true
schema:
type: string
- - name: procedure_name
+ - name: procedure_id
in : path
required: true
schema:
@@ -289,11 +423,18 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/APIResponse'
+ "404":
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "Graph Not found/Procedure Not found"
/v1/service/start:
post:
tags:
- - service
+ - AdminService/ServiceManagement
description: Start service on a specified graph
operationId: start_service
requestBody:
@@ -301,18 +442,25 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Service'
+ $ref: '#/components/schemas/StartServiceRequest'
responses:
'200':
description: successful operation
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/APIResponse'
+ '500':
+ description: Internal Error
+ content:
+ application/json:
+ schema:
+ type: string
+ example: "Internal Error"
/v1/service/stop:
post:
tags:
- - service
+ - AdminService/ServiceManagement
description: Stop current service
operationId: stop_service
responses:
@@ -321,11 +469,11 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/APIResponse'
/v1/service/restart:
post:
tags:
- - service
+ - AdminService/ServiceManagement
description: Start current service
operationId: restart_service
responses:
@@ -334,11 +482,11 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ApiResponse'
+ $ref: '#/components/schemas/APIResponse'
/v1/service/status:
get:
tags:
- - service
+ - AdminService/ServiceManagement
description: Get service status
operationId: get_service_status
responses:
@@ -348,63 +496,906 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ServiceStatus'
+ /v1/graph/{graph_id}/vertex:
+ get:
+ tags:
+ - GraphService/VertexManagement
+ summary: Get the vertex's properties with vertex primary key.
+ operationId: get_vertex
+ description: |
+ Get the properties for the specified vertex.
+ example:
+ ```http
+ GET /endpoint?param1=value1¶m2=value2 HTTP/1.1
+ Host: example.com
+ ```
+ parameters:
+ - name: graph_id
+ in: path
+ required: true
+ description: The name of the graph
+ schema:
+ type: string
+ - name: label
+ in: query
+ required: true
+ description: The label name of querying vertex.
+ schema:
+ type: string
+ - name: primary_key_value
+ in: query
+ required: true
+ description: The primary key value of querying vertex.
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ responses:
+ '200':
+ description: Found vertex
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VertexData'
+ example:
+ label: "person"
+ values:
+ - name: "id"
+ value: 1
+ - name: "age"
+ value: 23
+ - name: "name"
+ value: "amy"
+ '400':
+ description: Bad input parameter
+ '404':
+ description: Vertex not found or graph not found
+ '500':
+ description: Server internal error
+ post:
+ tags:
+ - GraphService/VertexManagement
+ summary: Add vertex to the graph
+ operationId: add_vertex
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ description: |
+ Add the provided vertex to the specified graph.
+ responses:
+ '200':
+ description: Successfully created vertex
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfully created vertex"
+ '400':
+ description: Invalid input vertex
+ '404':
+ description: Graph not found
+ '409':
+ description: Vertex already exists
+ '500':
+ description: Server internal error
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VertexRequest'
+ example:
+ label: "person"
+ primimary_key_value: 1
+ properties:
+ age: 2
+ name: "Bob"
+ description: Add vertex to graph.
+ put:
+ tags:
+ - GraphService/VertexManagement
+ summary: Update vertex's property
+ operationId: update_vertex
+ description: |
+ Remove the vertex from the specified graph.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VertexRequest'
+ example:
+ label: "person"
+ primary_key_value: 2
+ properties:
+ age: 24
+ name: "Cindy"
+ responses:
+ '200':
+ description: Successfully update vertex
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfully updated vertex"
+ '400':
+ description: Invalid input paramters
+ '404':
+ description: Vertex not exists
+ '500':
+ description: Server internal error
+ delete:
+ tags:
+ - GraphService/VertexManagement
+ summary: Remove vertex from the graph
+ operationId: delete_vertex
+ description: |
+ Remove the vertex from the specified graph.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ - name: label
+ in: query
+ required: true
+ description: The label name of querying vertex.
+ schema:
+ type: string
+ - name: primary_key_value
+ in: query
+ required: true
+ description: The value of the querying vertex's primary key
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ responses:
+ '200':
+ description: Successfully delete vertex
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfully delete vertex"
+ '400':
+ description: Invalid input vertex
+ '404':
+ description: Vertex not exists or Graph not exits.
+ '500':
+ description: Server internal error
+
+ /v1/graph/{graph_id}/edge:
+ get:
+ tags:
+ - GraphService/EdgeManagement
+ summary: Get the edge's properties with src and dst vertex primary keys.
+ operationId: get_edge
+ description: |
+ Get the properties for the specified vertex.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ - name: edge_label
+ in: query
+ required: true
+ description: The label name of querying edge.
+ schema:
+ type: string
+ example: created
+ - name: src_label
+ in: query
+ required: true
+ description: The label name of src vertex.
+ schema:
+ type: string
+ example: person
+ - name: src_primary_key_value
+ in: query
+ required: true
+ description: The primary key value of src vertex.
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ example: 1
+ - name: dst_label
+ in: query
+ required: true
+ description: The label name of dst vertex.
+ schema:
+ type: string
+ example: software
+ - name: dst_primary_key_value
+ in: query
+ required: true
+ description: The value of dst vertex's primary key
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ example: 3
+ responses:
+ '200':
+ description: Found Edge
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EdgeData'
+ example:
+ src_label: "person"
+ dst_label: "software"
+ edge_label: "created"
+ src_pk_value: 1
+ dst_pk_value: 3
+ properties:
+ - name: "weight"
+ value: 0.2
+ '400':
+ description: Bad input parameter
+ '404':
+ description: Edge not found or Graph not found
+ '500':
+ description: Server internal error
+ post:
+ tags:
+ - GraphService/EdgeManagement
+ summary: Add edge to the graph
+ operationId: add_edge
+ description: |
+ Add the edge to graph.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Successfully insert the edge
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfuly create edge"
+ '400':
+ description: Invalid input edge
+ '409':
+ description: edge already exists
+ '500':
+ description: Server internal error
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EdgeRequest'
+ example:
+ src_label: "person"
+ dst_label: "software"
+ edge_label: "created"
+ src_pk_name: "id"
+ src_pk_value: 1
+ dst_pk_name: "id"
+ dst_pk_value: 3
+ properties:
+ - name: "weight"
+ value: 0.2
+ put:
+ tags:
+ - GraphService/EdgeManagement
+ summary: Update edge's property
+ operationId: update_edge
+ description: |
+ Update the edge on the running graph.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EdgeRequest'
+ example:
+ src_label: "person"
+ dst_label: "software"
+ edge_label: "created"
+ src_pk_name: "id"
+ src_pk_value: 1
+ dst_pk_name: "id"
+ dst_pk_value: 3
+ properties:
+ - name: "weight"
+ value: 0.3
+ responses:
+ '200':
+ description: Successfully update edge
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfully update edge"
+ '400':
+ description: Invalid input paramters
+ '404':
+ description: Edge not exists
+ '500':
+ description: Server internal error
+ delete:
+ tags:
+ - GraphService/EdgeManagement
+ summary: Remove edge from the graph
+ operationId: delete_edge
+ description: |
+ Remove the edge from current graph.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ - name: src_label
+ in: query
+ required: true
+ description: The label name of src vertex.
+ schema:
+ type: string
+ example: person
+ - name: src_primary_key_value
+ in: query
+ required: true
+ description: The primary key value of src vertex.
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ # type: object
+ example: 1
+ - name: dst_label
+ in: query
+ required: true
+ description: The label name of dst vertex.
+ schema:
+ type: string
+ example: software
+ - name: dst_primary_key_value
+ in: query
+ required: true
+ description: The primary key value of dst vertex.
+ schema:
+ $ref: '#/components/schemas/AnyValue'
+ example: 3
+ responses:
+ '200':
+ description: Successfully delete edge
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/APIResponse'
+ example:
+ message: "Successfully delete edge"
+ '400':
+ description: Invalid input edge
+ '404':
+ description: Edge not exists or Graph not exits
+ '500':
+ description: Server internal error
+ /v1/graph/{graph_id}/query:
+ post:
+ tags:
+ - QueryService
+ summary: run queries on graph
+ operationId: proc_call
+ description: |
+ After the procedure is created, user can use this API to run the procedure.
+ TODO: a full example cypher->plan->json.
+ TODO: make sure typeinfo can be passed.
+ parameters:
+ - name: graph_id
+ in : path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/QueryRequest'
+ # application/json:
+ # schema:
+ # type: string
+ # examples:
+ # example1:
+ # value:
+ # type: "native/proc_call/adhoc"
+ # payload: |
+ # binary bytes
+ # example2:
+ # value:
+ # type: "native"
+ # payload: "0,123,david(encoded in binary)"
+ # example3:
+ # value:
+ # type: "adhoc"
+ # payload:
+ # "json[scan{}, edge_expand{}]"
+ # example4:
+ # value:
+ # type: "proc_call"
+ # payload:
+ # "json{proc_name: procA, params: []}"
+ responses:
+ '200':
+ description: Successfully runned. Empty if failed?
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CollectiveResults'
+ '500':
+ description: Server internal error
components:
schemas:
- Graph:
- x-body-name: graph
+ AnyValue: {}
+ # Corresponding to common.value, which store a any type value
+ TypedValue:
+ type: object
+ required:
+ - type
+ - value
+ properties:
+ type:
+ $ref: '#/components/schemas/GSDataType'
+ value:
+ $ref: '#/components/schemas/AnyValue'
+ # for query output
+ Element:
+ oneOf:
+ - $ref: '#/components/schemas/TypedValue'
+ Collection:
+ type: object
+ properties:
+ values:
+ type: array
+ items:
+ $ref: '#/components/schemas/Element'
+ KeyValue:
+ type: object
+ required:
+ - key
+ - value
+ properties:
+ key:
+ $ref: '#/components/schemas/TypedValue'
+ value:
+ $ref: '#/components/schemas/Element'
+ Column:
+ oneOf:
+ - $ref: '#/components/schemas/Element'
+ - $ref: '#/components/schemas/Collection'
+ - $ref: '#/components/schemas/KeyValue'
+ Record:
+ type: object
+ properties:
+ columns:
+ type: array
+ items:
+ $ref: '#/components/schemas/Column'
+ CollectiveResults:
+ type: object
+ properties:
+ records:
+ type: array
+ items:
+ $ref: '#/components/schemas/Record'
+ PrimitiveType:
+ x-body-name: primitive_type
+ type: object
+ required:
+ - primitive_type
+ properties:
+ primitive_type:
+ type: string
+ enum: [DT_SIGNED_INT32, DT_UNSIGNED_INT32, DT_SIGNED_INT64, DT_UNSIGNED_INT64,
+ DT_BOOL, DT_FLOAT, DT_DOUBLE]
+ example: DT_SIGNED_INT32
+ LongText:
+ x-body-name: long_text
+ type: object
+ required:
+ - long_text
+ properties:
+ long_text:
+ type: string
+ nullable: true
+ FixedChar:
+ x-body-name: fixed_char
+ type: object
+ required:
+ - char
+ properties:
+ char:
+ type: object
+ required:
+ - fixed_char
+ properties:
+ fixed_char:
+ type: integer
+ VarChar:
+ x-body-name: var_char
+ type: object
+ required:
+ - var_char
+ properties:
+ var_char:
+ type: object
+ required:
+ - max_length
+ properties:
+ max_length:
+ type: integer
+ StringType:
+ x-body-name: string_type
+ type: object
+ required:
+ - string
+ properties:
+ string:
+ oneOf:
+ - $ref: '#/components/schemas/LongText'
+ - $ref: '#/components/schemas/FixedChar'
+ - $ref: '#/components/schemas/VarChar'
+ TimeStampType:
+ x-body-name: time_stamp_type
+ type: object
+ required:
+ - timestamp
+ properties:
+ timestamp:
+ type: string
+ TemporalType:
+ x-body-name: temporal_type
type: object
+ oneOf:
+ - $ref: '#/components/schemas/TimeStampType'
+ GSDataType:
+ x-body-name: gs_data_type
+ oneOf:
+ - $ref: '#/components/schemas/PrimitiveType'
+ - $ref: '#/components/schemas/StringType'
+ - $ref: '#/components/schemas/TemporalType'
+ Property:
+ x-body-name: property
+ type: object
+ required:
+ - name
+ - value
+ properties:
+ name:
+ type: string
+ example: "id"
+ value:
+ $ref: '#/components/schemas/AnyValue'
+ # type: object
+ PropertyArray:
+ x-body-name: property_array
+ type: object
+ required:
+ - properties
+ properties:
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/Property'
+ Parameter:
+ x-body-name: parameter
+ type: object
+ required:
+ - name
+ - type
properties:
name:
type: string
+ example: param1
+ type:
+ $ref: '#/components/schemas/GSDataType'
+ VertexRequest:
+ x-body-name: vertex_request
+ type: object
+ required:
+ - label
+ - primary_key_value
+ properties:
+ label:
+ type: string
+ example: person
+ primary_key_value:
+ $ref: '#/components/schemas/AnyValue'
+ properties:
+ $ref: '#/components/schemas/PropertyArray'
+ VertexData:
+ x-body-name: vertex_data
+ type: object
+ required:
+ - label
+ properties:
+ label:
+ type: string
+ example: person
+ values:
+ type: array
+ items:
+ $ref: '#/components/schemas/Property'
+ EdgeData:
+ x-body-name: edge_data
+ type: object
+ required:
+ - src_label
+ - dst_label
+ - edge_label
+ - src_primary_key_value
+ - dst_primary_key_value
+ - properties
+ properties:
+ src_label:
+ type: string
+ example: person
+ dst_label:
+ type: string
+ example: software
+ edge_label:
+ type: string
+ example: created
+ src_primary_key_value:
+ $ref: '#/components/schemas/AnyValue'
+ dst_primary_key_value:
+ $ref: '#/components/schemas/AnyValue'
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/Property'
+ EdgeRequest:
+ x-body-name: edge_request
+ type: object
+ required:
+ - src_label
+ - dst_label
+ - edge_label
+ - src_primary_key_value
+ - dst_primary_key_value
+ properties:
+ src_label:
+ type: string
+ example: person
+ dst_label:
+ type: string
+ example: software
+ edge_label:
+ type: string
+ example: created
+ src_primary_key_value:
+ $ref: '#/components/schemas/AnyValue'
+ dst_primary_key_value:
+ $ref: '#/components/schemas/AnyValue'
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/Property'
+ QueryRequest:
+ x-body-name: query_request
+ type: object
+ required:
+ - query_name
+ properties:
+ query_name:
+ type: string
+ example: ic1
+ arguments:
+ type: array
+ items:
+ $ref: '#/components/schemas/TypedValue'
+ CreateProcedureRequest:
+ x-body-name: create_procedure_request
+ type: object
+ required:
+ - name
+ - type
+ - query
+ properties:
+ name:
+ type: string
+ example: query1
+ description:
+ type: string
+ example: A sample stored procedure
+ type:
+ type: string
+ enum:
+ - cpp
+ - cypher
+ query:
+ type: string
+ example: "MATCH(a) return COUNT(a);"
+ CreateProcedureResponse:
+ type: object
+ x-body-name: create_procedure_response
+ required:
+ - procedure_id
+ properties:
+ procedure_id:
+ type: string
+ example: proc1
+ StoredProcedureMeta:
+ x-body-name: stored_procedure_meta
+ allOf:
+ - $ref: '#/components/schemas/CreateProcedureRequest'
+ - type: object
+ properties:
+ id:
+ type: string
+ example: The unique identifier of procedure, currently is same with name.
+ library:
+ type: string
+ example: /path/to/library
+ params:
+ type: array
+ items:
+ $ref: '#/components/schemas/Parameter'
+ returns:
+ type: array
+ items:
+ $ref: '#/components/schemas/Parameter'
+ enable:
+ type: boolean
+ example : true
+ GetProcedureResponse:
+ x-body-name: get_procedure_response
+ allOf:
+ - $ref: '#/components/schemas/StoredProcedureMeta'
+ - type: object
+ properties:
+ bound_graph:
+ type: string
+ runnable:
+ type: boolean
+ creation_time:
+ type: integer
+ UpdateProcedureRequest:
+ x-body-name: update_procedure_request
+ type: object
+ properties:
+ description:
+ type: string
+ example: A sample stored procedure
+ CreateGraphResponse:
+ x-body-name: create_graph_response
+ type: object
+ properties:
+ graph_id:
+ type: string
+ example: "1"
+ CreateGraphRequest:
+ x-body-name: create_graph_request
+ type: object
+ properties:
+ name:
+ type: string
+ example: modern_graph
+ description:
+ type: string
+ example: A default description
+ stored_procedures:
+ type: array
+ items:
+ $ref: '#/components/schemas/CreateProcedureRequest'
+ schema:
+ $ref: '#/components/schemas/CreateGraphSchemaRequest'
+ APIResponse:
+ x-body-name: api_response
+ type: string
+ example: "Response string"
+ GetGraphResponse:
+ x-body-name: get_graph_response
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ description:
+ type: string
store_type:
type: string
enum:
- mutable_csr
+ creation_time:
+ type: integer
+ example: 11223444
+ data_update_time:
+ type: integer
+ example: 11123445
stored_procedures:
- type: object
- properties:
- directory:
- type: string
- enum:
- - plugins
+ type: array
+ items:
+ $ref: '#/components/schemas/GetProcedureResponse'
schema:
- $ref: '#/components/schemas/Schema'
- Schema:
- x-body-name: schema
+ $ref: '#/components/schemas/GetGraphSchemaResponse'
+ data_import_config:
+ $ref: '#/components/schemas/SchemaMapping'
+ CreateGraphSchemaRequest:
+ x-body-name: create_graph_schema_request
type: object
properties:
vertex_types:
type: array
items:
- $ref: '#/components/schemas/VertexType'
+ $ref: '#/components/schemas/CreateVertexType'
edge_types:
type: array
items:
- $ref: '#/components/schemas/EdgeType'
- VertexType:
- x-body-name: vertex_type
+ $ref: '#/components/schemas/CreateEdgeType'
+ GetGraphSchemaResponse:
+ x-body-name: get_graph_schema_response
type: object
properties:
- type_id:
- type: integer
- format: int32
- type_name:
- type: string
- properties:
+ vertex_types:
type: array
items:
- $ref: '#/components/schemas/Property'
+ $ref: '#/components/schemas/GetVertexType'
+ edge_types:
+ type: array
+ items:
+ $ref: '#/components/schemas/GetEdgeType'
+ BaseVertexType:
+ type: object
+ properties:
+ type_name:
+ type: string
primary_keys:
type: array
items:
type: string
- EdgeType:
- x-body-name: edge_type
+ x_csr_params:
+ type: object
+ description: Used for storage optimization
+ properties:
+ max_vertex_num:
+ type: integer
+ CreateVertexType:
+ x-body-name: create_vertex_type
+ allOf:
+ - $ref: '#/components/schemas/BaseVertexType'
+ - type: object
+ properties:
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/CreatePropertyMeta'
+ GetVertexType:
+ x-body-name: get_vertex_type
+ allOf:
+ - $ref: '#/components/schemas/BaseVertexType'
+ - type: object
+ properties:
+ type_id:
+ type: integer
+ format: int32
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/GetPropertyMeta'
+ description:
+ type: string
+ BaseEdgeType:
type: object
properties:
- type_id:
- type: integer
- format: int32
type_name:
type: string
vertex_type_pair_relations:
@@ -433,31 +1424,53 @@ components:
- ONLY_IN
- ONLY_OUT
- BOTH_OUT_IN
- properties:
- type: array
- items:
- $ref: '#/components/schemas/Property'
- Property:
- x-body-name: property
+ sort_on_compaction:
+ type: boolean
+ CreateEdgeType:
+ x-body-name: create_edge_type
+ allOf:
+ - $ref: '#/components/schemas/BaseEdgeType'
+ - type: object
+ properties:
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/CreatePropertyMeta'
+ GetEdgeType:
+ x-body-name: get_edge_type
+ allOf:
+ - $ref: '#/components/schemas/BaseEdgeType'
+ - type: object
+ properties:
+ type_id:
+ type: integer
+ format: int32
+ description:
+ type: string
+ properties:
+ type: array
+ items:
+ $ref: '#/components/schemas/GetPropertyMeta'
+ BasePropertyMeta:
type: object
properties:
- property_id:
- type: integer
- format: int32
property_name:
type: string
- property_type:
- type: object
- description: Property type
+ property_type:
+ $ref: '#/components/schemas/GSDataType'
+ CreatePropertyMeta:
+ x-body-name: create_property_meta
+ allOf:
+ - $ref: '#/components/schemas/BasePropertyMeta'
+ GetPropertyMeta:
+ x-body-name: get_property_meta
+ allOf:
+ - $ref: '#/components/schemas/BasePropertyMeta'
+ - type: object
properties:
- primitive_type:
- type: string
- enum:
- - DT_DOUBLE
- - DT_STRING
- - DT_SIGNED_INT32
- - DT_SIGNED_INT64
- - DT_DATE32
+ property_id:
+ type: integer
+ format: int32
SchemaMapping:
x-body-name: schema_mapping
type: object
@@ -467,16 +1480,6 @@ components:
loading_config:
type: object
properties:
- data_source:
- type: object
- properties:
- scheme:
- type: string
- enum:
- - file
- - oss
- - s3
- - hdfs
import_option:
type: string
enum:
@@ -508,6 +1511,7 @@ components:
type: array
items:
type: string
+ example: file:///path/to/person.csv
column_mappings:
type: array
items:
@@ -544,6 +1548,9 @@ components:
format: int32
name:
type: string
+ property:
+ type: string # the primary key's name to map
+ example: id
destination_vertex_mappings:
type: array
items:
@@ -558,6 +1565,9 @@ components:
format: int32
name:
type: string
+ property:
+ type: string
+ example: id
column_mappings:
type: array
items:
@@ -577,49 +1587,10 @@ components:
property:
type: string
description: must align with the schema
- Procedure:
- x-body-name: procedure
- type: object
+ StartServiceRequest:
+ x-body-name: start_service_request
properties:
- name:
- type: string
- bound_graph:
- type: string
- description:
- type: string
- type:
- type: string
- enum:
- - cpp
- - cypher
- query:
- type: string
- enable:
- type: boolean
- runnable:
- type: boolean
- params:
- type: array
- items:
- type: object
- properties:
- name:
- type: string
- type:
- type: string
- returns:
- type: array
- items:
- type: object
- properties:
- name:
- type: string
- type:
- type: string
- Service:
- x-body-name: service
- properties:
- graph_name:
+ graph_id:
type: string
ServiceStatus:
x-body-name: service_status
@@ -627,7 +1598,7 @@ components:
properties:
status:
type: string
- graph_name:
+ graph_id:
type: string
bolt_port:
type: integer
@@ -635,6 +1606,9 @@ components:
hqps_port:
type: integer
format: int32
+ gremlin_port:
+ type: integer
+ format: int32
JobResponse:
type: object
x-body-name: job_response
@@ -645,7 +1619,7 @@ components:
type: object
x-body-name: job_status
properties:
- job_id:
+ id:
type: string
type:
type: string
@@ -668,6 +1642,4 @@ components:
description: URL or log string
detail:
type: object
- additionalProperties: true
- ApiResponse:
- type: string
+ additionalProperties: true
\ No newline at end of file
diff --git a/flex/tests/hqps/hqps_sdk_test.sh b/flex/tests/hqps/hqps_sdk_test.sh
new file mode 100644
index 000000000000..8aca2aacc4bb
--- /dev/null
+++ b/flex/tests/hqps/hqps_sdk_test.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+# Copyright 2020 Alibaba Group Holding Limited.
+#
+# Licensed 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.
+set -e
+SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
+FLEX_HOME=${SCRIPT_DIR}/../../
+SERVER_BIN=${FLEX_HOME}/build/bin/interactive_server
+GIE_HOME=${FLEX_HOME}/../interactive_engine/
+ADMIN_PORT=7777
+QUERY_PORT=10000
+
+#
+if [ ! $# -eq 3 ]; then
+ echo "only receives: $# args, need 3"
+ echo "Usage: $0 "
+ exit 1
+fi
+
+INTERACTIVE_WORKSPACE=$1
+ENGINE_CONFIG_PATH=$2
+SDK_TYPE=$3
+if [ ! -d ${INTERACTIVE_WORKSPACE} ]; then
+ echo "INTERACTIVE_WORKSPACE: ${INTERACTIVE_WORKSPACE} not exists"
+ exit 1
+fi
+if [ ! -f ${ENGINE_CONFIG_PATH} ]; then
+ echo "ENGINE_CONFIG: ${ENGINE_CONFIG_PATH} not exists"
+ exit 1
+fi
+
+# if SDK_TYPE != java or python, exit
+if [ ${SDK_TYPE} != "java" ] && [ ${SDK_TYPE} != "python" ]; then
+ echo "SDK_TYPE: ${SDK_TYPE} not supported, only support java or python"
+ exit 1
+fi
+
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+err() {
+ echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] -ERROR- $* ${NC}" >&2
+}
+
+info() {
+ echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] -INFO- $* ${NC}"
+}
+
+
+kill_service(){
+ info "Kill Service first"
+ ps -ef | grep "interactive_server" | awk '{print $2}' | xargs kill -9 || true
+ ps -ef | grep "compiler" | awk '{print $2}' | xargs kill -9 || true
+ sleep 3
+ # check if service is killed
+ info "Kill Service success"
+}
+
+# kill service when exit
+trap kill_service EXIT
+
+# start engine service and load ldbc graph
+start_engine_service(){
+ #check SERVER_BIN exists
+ if [ ! -f ${SERVER_BIN} ]; then
+ err "SERVER_BIN not found"
+ exit 1
+ fi
+
+ cmd="${SERVER_BIN} -c ${ENGINE_CONFIG_PATH} --enable-admin-service true "
+ cmd="${cmd} -w ${INTERACTIVE_WORKSPACE} --start-compiler true &"
+
+ echo "Start engine service with command: ${cmd}"
+ eval ${cmd}
+ sleep 5
+ #check interactive_server is running, if not, exit
+ ps -ef | grep "interactive_server" | grep -v grep
+
+ info "Start engine service success"
+}
+
+run_java_sdk_test(){
+ echo "run java sdk test"
+ pushd ${FLEX_HOME}/interactive/sdk/java/
+ cmd="mvn test -Dtest=com.alibaba.graphscope.interactive.client.DriverTest"
+ echo "Start java sdk test: ${cmd}"
+ eval ${cmd} || (err "java sdk test failed" && exit 1)
+ info "Finish java sdk test"
+ popd
+}
+
+run_python_sdk_test(){
+ echo "run python sdk test"
+ pushd ${FLEX_HOME}/interactive/sdk/python/
+ pip3 install -r requirements.txt
+ pip3 install -r test-requirements.txt
+ cmd="python3 -m pytest -s test/test_driver.py"
+ echo "Start python sdk test: ${cmd}"
+ eval ${cmd} || (err "java python test failed" && exit 1)
+ info "Finish python sdk test"
+ popd
+}
+
+kill_service
+start_engine_service
+if [ ${SDK_TYPE} == "java" ]; then
+ run_java_sdk_test
+elif [ ${SDK_TYPE} == "python" ]; then
+ run_python_sdk_test
+else
+ err "SDK_TYPE: ${SDK_TYPE} not supported, only support java or python"
+ exit 1
+fi
+kill_service
+
+
+
+