From d3542e50dd6404f2753fd64102db2943188d1dab Mon Sep 17 00:00:00 2001 From: Zhang Lei Date: Tue, 30 Apr 2024 15:18:59 +0800 Subject: [PATCH] feat(interactive): Introduce Interactive Java/Python SDK (#3602) Introduce the SDK of GraphScope Interactive. Including `Java SDK` and `Python SDK`. The most part of the code in SDKs are automatically generated from the OpenAPI specification yaml(graphscope_interactive.yaml) using tools provided by [OpenAPI](https://openapi-generator.tech). We integrate the generated code and cypher/gremlin driver with some wrapper code. - [x] The generated code `SHOULD not` be included in the codebase, user can refer to `flex/interactive/sdk/README.md` to generate the SDKs if interested. - [x] Providing some examples about how to use the SDKs. - [x] Add tests in Interactive CI - [x] Also add docs for Interactive SDK. For example usage of Python/Java SDK, see `BasicExample.java` and `basic_example.py`. --- .github/workflows/hqps-db-ci.yml | 29 + .gitignore | 35 + docs/Makefile | 12 +- docs/flex/interactive/dev_guide.md | 4 +- .../interactive/development/java/java_sdk.md | 202 ++ .../development/python/python_sdk.md | 175 ++ .../{admin_service.md => restful_api.md} | 4 +- docs/flex/interactive/sdk.md | 12 + docs/flex/interactive/tools.md | 11 + docs/flex/interactive_intro.md | 1 + flex/interactive/sdk/README.md | 50 + .../java/interactive-sdk-example/pom.xml | 29 + .../com/alibaba/graphscope/BasicExample.java | 259 ++ .../sdk/examples/python/basic_example.py | 220 ++ flex/interactive/sdk/generate_sdk.sh | 146 ++ .../sdk/java/.openapi-generator-ignore | 28 + flex/interactive/sdk/java/README.md | 96 + flex/interactive/sdk/java/api/openapi.yaml | 2172 +++++++++++++++++ flex/interactive/sdk/java/pom.xml | 364 +++ .../graphscope/interactive/client/Driver.java | 173 ++ .../interactive/client/EdgeInterface.java | 44 + .../interactive/client/GraphInterface.java | 36 + .../interactive/client/JobInterface.java | 32 + .../client/ProcedureInterface.java | 41 + .../client/QueryServiceInterface.java | 32 + .../interactive/client/Session.java | 25 + .../interactive/client/VertexInterface.java | 32 + .../interactive/client/common/Result.java | 64 + .../interactive/client/common/Status.java | 101 + .../client/impl/DefaultSession.java | 439 ++++ .../interactive/client/DriverTest.java | 296 +++ .../sdk/python/.openapi-generator-ignore | 32 + flex/interactive/sdk/python/README.md | 66 + .../sdk/python/interactive_sdk/__init__.py | 0 .../python/interactive_sdk/client/__init__.py | 6 + .../python/interactive_sdk/client/driver.py | 98 + .../python/interactive_sdk/client/result.py | 72 + .../python/interactive_sdk/client/session.py | 540 ++++ .../python/interactive_sdk/client/status.py | 107 + flex/interactive/sdk/python/requirements.txt | 7 + flex/interactive/sdk/python/setup.cfg | 2 + flex/interactive/sdk/python/setup.py | 53 + .../sdk/python/test-requirements.txt | 3 + .../sdk/python/test/test_driver.py | 237 ++ flex/openapi/openapi_interactive.yaml | 1382 +++++++++-- flex/tests/hqps/hqps_sdk_test.sh | 129 + 46 files changed, 7689 insertions(+), 209 deletions(-) create mode 100644 docs/flex/interactive/development/java/java_sdk.md create mode 100644 docs/flex/interactive/development/python/python_sdk.md rename docs/flex/interactive/development/{admin_service.md => restful_api.md} (98%) create mode 100644 docs/flex/interactive/sdk.md create mode 100644 docs/flex/interactive/tools.md create mode 100644 flex/interactive/sdk/README.md create mode 100644 flex/interactive/sdk/examples/java/interactive-sdk-example/pom.xml create mode 100644 flex/interactive/sdk/examples/java/interactive-sdk-example/src/main/java/com/alibaba/graphscope/BasicExample.java create mode 100644 flex/interactive/sdk/examples/python/basic_example.py create mode 100755 flex/interactive/sdk/generate_sdk.sh create mode 100644 flex/interactive/sdk/java/.openapi-generator-ignore create mode 100644 flex/interactive/sdk/java/README.md create mode 100644 flex/interactive/sdk/java/api/openapi.yaml create mode 100644 flex/interactive/sdk/java/pom.xml create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Driver.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/EdgeInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/GraphInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/JobInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/ProcedureInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/QueryServiceInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Session.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/VertexInterface.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Result.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Status.java create mode 100644 flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/impl/DefaultSession.java create mode 100644 flex/interactive/sdk/java/src/test/java/com/alibaba/graphscope/interactive/client/DriverTest.java create mode 100644 flex/interactive/sdk/python/.openapi-generator-ignore create mode 100644 flex/interactive/sdk/python/README.md create mode 100644 flex/interactive/sdk/python/interactive_sdk/__init__.py create mode 100644 flex/interactive/sdk/python/interactive_sdk/client/__init__.py create mode 100644 flex/interactive/sdk/python/interactive_sdk/client/driver.py create mode 100644 flex/interactive/sdk/python/interactive_sdk/client/result.py create mode 100644 flex/interactive/sdk/python/interactive_sdk/client/session.py create mode 100644 flex/interactive/sdk/python/interactive_sdk/client/status.py create mode 100644 flex/interactive/sdk/python/requirements.txt create mode 100644 flex/interactive/sdk/python/setup.cfg create mode 100644 flex/interactive/sdk/python/setup.py create mode 100644 flex/interactive/sdk/python/test-requirements.txt create mode 100644 flex/interactive/sdk/python/test/test_driver.py create mode 100644 flex/tests/hqps/hqps_sdk_test.sh diff --git a/.github/workflows/hqps-db-ci.yml b/.github/workflows/hqps-db-ci.yml index fc46e2437265..13b16950763b 100644 --- a/.github/workflows/hqps-db-ci.yml +++ b/.github/workflows/hqps-db-ci.yml @@ -134,6 +134,35 @@ jobs: bash hqps_admin_test.sh ${TMP_INTERACTIVE_WORKSPACE} ./engine_config_test.yaml ${GS_TEST_DIR} sed -i 's/default_graph: modern_graph/default_graph: ldbc/g' ./engine_config_test.yaml + - name: Build and test Interactive Java/Python SDK + env: + FLEX_DATA_DIR: ${{ github.workspace }}/flex/interactive/examples/modern_graph + TMP_INTERACTIVE_WORKSPACE: /tmp/temp_workspace + LD_LIBRARY_PATH: /usr/local/lib + run: | + cd ${GITHUB_WORKSPACE}/flex/interactive/sdk/ + + bash generate_sdk.sh -g java + bash generate_sdk.sh -g python + cd java + mvn clean install -DskipTests + cd ../python + pip3 install -r test-requirements.txt + pip3 install . + + rm -rf ${TMP_INTERACTIVE_WORKSPACE} + cd ${GITHUB_WORKSPACE}/flex/build/ + SCHEMA_FILE=${GITHUB_WORKSPACE}/flex/interactive/examples/modern_graph/graph.yaml + BULK_LOAD_FILE=${GITHUB_WORKSPACE}/flex/interactive/examples/modern_graph/bulk_load.yaml + mkdir -p ${TMP_INTERACTIVE_WORKSPACE}/data/modern_graph/ + cp ${SCHEMA_FILE} ${TMP_INTERACTIVE_WORKSPACE}/data/modern_graph/graph.yaml + GLOG_v=10 ./bin/bulk_loader -g ${SCHEMA_FILE} -l ${BULK_LOAD_FILE} -d ${TMP_INTERACTIVE_WORKSPACE}/data/modern_graph/indices/ + cd ${GITHUB_WORKSPACE}/flex/tests/hqps + sed -i 's/default_graph: ldbc/default_graph: modern_graph/g' ./engine_config_test.yaml + bash hqps_sdk_test.sh ${TMP_INTERACTIVE_WORKSPACE} ./engine_config_test.yaml java + bash hqps_sdk_test.sh ${TMP_INTERACTIVE_WORKSPACE} ./engine_config_test.yaml python + sed -i 's/default_graph: modern_graph/default_graph: ldbc/g' ./engine_config_test.yaml + - name: Sample Query test env: GS_TEST_DIR: ${{ github.workspace }}/gstest diff --git a/.gitignore b/.gitignore index d77f8fb88c8f..e9bfb63cc059 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,10 @@ docs/source/reference/ docs/reference/generated/ docs/reference/networkx/generated/ docs/_build/ +docs/flex/interactive/development/java +!docs/flex/interactive/development/java/java_sdk.mod +docs/flex/interactive/development/python +!docs/flex/interactive/development/python/python_sdk.mod analytical_engine/build/ @@ -100,4 +104,35 @@ flex/interactive/examples/sf0.1-raw/ flex/interactive/.running flex/interactive/.env +# interactive sdk related +flex/interactive/sdk/java/.github/ +flex/interactive/sdk/java/.gitignore +flex/interactive/sdk/java/docs/ +flex/interactive/sdk/java/build.sbt +flex/interactive/sdk/java/build.gradle +flex/interactive/sdk/java/gradle.properties +flex/interactive/sdk/java/gradlew.bat +flex/interactive/sdk/java/gradlew +flex/interactive/sdk/java/.travis.yml +flex/interactive/sdk/java/git_push.sh +flex/interactive/sdk/java/gradle/ +flex/interactive/sdk/java/.openapi-generator/* +flex/interactive/sdk/java/src/main/AndroidManifest.xml +flex/interactive/sdk/java/.openapi-generate/ +flex/interactive/sdk/java/settings.gradle +flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/openapi/ + +flex/interactive/sdk/python/.github/ +flex/interactive/sdk/python/.gitignore +flex/interactive/sdk/python/docs/ +flex/interactive/sdk/python/git_push.sh +flex/interactive/sdk/python/.gitlab-ci.yml +flex/interactive/sdk/python/.openapi-generator/ +flex/interactive/sdk/python/dist/ +flex/interactive/sdk/python/.travis.yml +flex/interactive/sdk/python/tox.ini +flex/interactive/sdk/python/pyproject.toml +flex/interactive/sdk/python/interactive_sdk/openapi/ + + **/.cache/ diff --git a/docs/Makefile b/docs/Makefile index e05e147cdddc..02b3348a8088 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -51,6 +51,16 @@ clean: @mkdir -p "$(BUILDDIR)/${TAG}/$@/reference/" @cat Doxyfile-flex | sed 's/_build\/latest\/html\/reference\//$(BUILDDIR)\/${TAG}\/$@\/reference\//g' | doxygen - -doxygen: +interactive-sdk: + @echo "Building interactive sdk doc to $(BUILDDIR)/${TAG}/interactive-sdk" + @cd ${WORKING_DIR}/../flex/interactive/sdk/ && ./generate_sdk.sh -g java \ + && ./generate_sdk.sh -g python + + # copy the generated files to ${WORKING_DIR}/flex/interactive/development/ + @cp -r ${WORKING_DIR}/../flex/interactive/sdk/java/docs/* ${WORKING_DIR}/flex/interactive/development/java/ + @cp -r ${WORKING_DIR}/../flex/interactive/sdk/python/docs/* ${WORKING_DIR}/flex/interactive/development/python/ + + +doxygen: interactive-sdk @mkdir -p _build @doxygen Doxyfile diff --git a/docs/flex/interactive/dev_guide.md b/docs/flex/interactive/dev_guide.md index 0ef89c98c686..6827fa289e9e 100644 --- a/docs/flex/interactive/dev_guide.md +++ b/docs/flex/interactive/dev_guide.md @@ -10,5 +10,7 @@ maxdepth: 2 --- development/cypher_procedure development/cpp_procedure -development/admin_service +development/restful_api +development/java/java_sdk +development/python/python_sdk ``` \ No newline at end of file diff --git a/docs/flex/interactive/development/java/java_sdk.md b/docs/flex/interactive/development/java/java_sdk.md new file mode 100644 index 000000000000..6afebc263fe8 --- /dev/null +++ b/docs/flex/interactive/development/java/java_sdk.md @@ -0,0 +1,202 @@ +# Java SDK Reference + +The Interactive Java SDK Reference is a comprehensive guide for developers looking to integrate the Interactive service into their Java applications. This SDK allows users to seamlessly connect to Interactive and leverage its powerful features for graph management, stored procedure management, and query execution. + + +## Requirements + +Building the API client library requires: +1. Java 1.8+ +2. Maven (3.8.3+)/Gradle (7.2+) + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +git clone https://github.com/alibaba/GraphScope.git +cd GraphScope/flex/interactive/sdk/java +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + com.alibaba.graphscope + interactive-java-sdk + 0.0.3 + compile + +``` + +### Others + +At first generate the JAR by executing: + +```shell +mvn clean package +``` + +Then manually install the following JARs: + +* `target/interactive-java-sdk-0.0.3.jar` +* `target/lib/*.jar` + +## 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. + +```java +package com.alibaba.graphscope; + +import com.alibaba.graphscope.interactive.client.Driver; +import com.alibaba.graphscope.interactive.client.Session; + +public class GettingStarted { + public static void main(String[] args) { + //get endpoint from command line + if (args.length != 1) { + System.out.println("Usage: "); + return; + } + String endpoint = args[0]; + Driver driver = Driver.connect(endpoint); + Session session = driver.session(); + + // start a query + // run cypher query + try (org.neo4j.driver.Session neo4jSession = driver.getNeo4jSession()) { + org.neo4j.driver.Result result = neo4jSession.run("MATCH(a) return COUNT(a);"); + System.out.println("result: " + result.toString()); + } + return; + } +} +``` + +For more a more detailed example, please refer to [Java SDK Example](https://github.com/alibaba/GraphScope/flex/interactive/sdk/examples/java/interactive-sdk-example/) + +## Documentation for Service APIs + +All URIs are relative to `${INTERACTIVE_ENDPOINT}` + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*AdminServiceGraphManagementApi* | [**createDataloadingJob**](./AdminServiceGraphManagementApi.md#createDataloadingJob) | **POST** /v1/graph/{graph_id}/dataloading | +*AdminServiceGraphManagementApi* | [**createGraph**](./AdminServiceGraphManagementApi.md#createGraph) | **POST** /v1/graph | +*AdminServiceGraphManagementApi* | [**deleteGraph**](./AdminServiceGraphManagementApi.md#deleteGraph) | **DELETE** /v1/graph/{graph_id} | +*AdminServiceGraphManagementApi* | [**getGraph**](./AdminServiceGraphManagementApi.md#getGraph) | **GET** /v1/graph/{graph_id} | +*AdminServiceGraphManagementApi* | [**getSchema**](./AdminServiceGraphManagementApi.md#getSchema) | **GET** /v1/graph/{graph_id}/schema | +*AdminServiceGraphManagementApi* | [**listGraphs**](./AdminServiceGraphManagementApi.md#listGraphs) | **GET** /v1/graph | +*AdminServiceJobManagementApi* | [**deleteJobById**](./AdminServiceJobManagementApi.md#deleteJobById) | **DELETE** /v1/job/{job_id} | +*AdminServiceJobManagementApi* | [**getJobById**](./AdminServiceJobManagementApi.md#getJobById) | **GET** /v1/job/{job_id} | +*AdminServiceJobManagementApi* | [**listJobs**](./AdminServiceJobManagementApi.md#listJobs) | **GET** /v1/job | +*AdminServiceProcedureManagementApi* | [**createProcedure**](./AdminServiceProcedureManagementApi.md#createProcedure) | **POST** /v1/graph/{graph_id}/procedure | +*AdminServiceProcedureManagementApi* | [**deleteProcedure**](./AdminServiceProcedureManagementApi.md#deleteProcedure) | **DELETE** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceProcedureManagementApi* | [**getProcedure**](./AdminServiceProcedureManagementApi.md#getProcedure) | **GET** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceProcedureManagementApi* | [**listProcedures**](./AdminServiceProcedureManagementApi.md#listProcedures) | **GET** /v1/graph/{graph_id}/procedure | +*AdminServiceProcedureManagementApi* | [**updateProcedure**](./AdminServiceProcedureManagementApi.md#updateProcedure) | **PUT** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceServiceManagementApi* | [**getServiceStatus**](./AdminServiceServiceManagementApi.md#getServiceStatus) | **GET** /v1/service/status | +*AdminServiceServiceManagementApi* | [**restartService**](./AdminServiceServiceManagementApi.md#restartService) | **POST** /v1/service/restart | +*AdminServiceServiceManagementApi* | [**startService**](./AdminServiceServiceManagementApi.md#startService) | **POST** /v1/service/start | +*AdminServiceServiceManagementApi* | [**stopService**](./AdminServiceServiceManagementApi.md#stopService) | **POST** /v1/service/stop | +*GraphServiceEdgeManagementApi* | [**addEdge**](./GraphServiceEdgeManagementApi.md#addEdge) | **POST** /v1/graph/{graph_id}/edge | Add edge to the graph +*GraphServiceEdgeManagementApi* | [**deleteEdge**](./GraphServiceEdgeManagementApi.md#deleteEdge) | **DELETE** /v1/graph/{graph_id}/edge | Remove edge from the graph +*GraphServiceEdgeManagementApi* | [**getEdge**](./GraphServiceEdgeManagementApi.md#getEdge) | **GET** /v1/graph/{graph_id}/edge | Get the edge's properties with src and dst vertex primary keys. +*GraphServiceEdgeManagementApi* | [**updateEdge**](./GraphServiceEdgeManagementApi.md#updateEdge) | **PUT** /v1/graph/{graph_id}/edge | Update edge's property +*GraphServiceVertexManagementApi* | [**addVertex**](./GraphServiceVertexManagementApi.md#addVertex) | **POST** /v1/graph/{graph_id}/vertex | Add vertex to the graph +*GraphServiceVertexManagementApi* | [**deleteVertex**](./GraphServiceVertexManagementApi.md#deleteVertex) | **DELETE** /v1/graph/{graph_id}/vertex | Remove vertex from the graph +*GraphServiceVertexManagementApi* | [**getVertex**](./GraphServiceVertexManagementApi.md#getVertex) | **GET** /v1/graph/{graph_id}/vertex | Get the vertex's properties with vertex primary key. +*GraphServiceVertexManagementApi* | [**updateVertex**](./GraphServiceVertexManagementApi.md#updateVertex) | **PUT** /v1/graph/{graph_id}/vertex | Update vertex's property +*QueryServiceApi* | [**procCall**](./QueryServiceApi.md#procCall) | **POST** /v1/graph/{graph_id}/query | run queries on graph + + +## Documentation for Data Structures + + - [BaseEdgeType](./BaseEdgeType.md) + - [BaseEdgeTypeVertexTypePairRelationsInner](./BaseEdgeTypeVertexTypePairRelationsInner.md) + - [BaseEdgeTypeVertexTypePairRelationsInnerXCsrParams](./BaseEdgeTypeVertexTypePairRelationsInnerXCsrParams.md) + - [BasePropertyMeta](./BasePropertyMeta.md) + - [BaseVertexType](./BaseVertexType.md) + - [BaseVertexTypeXCsrParams](./BaseVertexTypeXCsrParams.md) + - [Collection](./Collection.md) + - [CollectiveResults](./CollectiveResults.md) + - [Column](./Column.md) + - [ColumnMapping](./ColumnMapping.md) + - [CreateEdgeType](./CreateEdgeType.md) + - [CreateGraphRequest](./CreateGraphRequest.md) + - [CreateGraphResponse](./CreateGraphResponse.md) + - [CreateGraphSchemaRequest](./CreateGraphSchemaRequest.md) + - [CreateProcedureRequest](./CreateProcedureRequest.md) + - [CreateProcedureResponse](./CreateProcedureResponse.md) + - [CreatePropertyMeta](./CreatePropertyMeta.md) + - [CreateVertexType](./CreateVertexType.md) + - [EdgeData](./EdgeData.md) + - [EdgeMapping](./EdgeMapping.md) + - [EdgeMappingDestinationVertexMappingsInner](./EdgeMappingDestinationVertexMappingsInner.md) + - [EdgeMappingSourceVertexMappingsInner](./EdgeMappingSourceVertexMappingsInner.md) + - [EdgeMappingSourceVertexMappingsInnerColumn](./EdgeMappingSourceVertexMappingsInnerColumn.md) + - [EdgeMappingTypeTriplet](./EdgeMappingTypeTriplet.md) + - [EdgeRequest](./EdgeRequest.md) + - [Element](./Element.md) + - [FixedChar](./FixedChar.md) + - [FixedCharChar](./FixedCharChar.md) + - [GSDataType](./GSDataType.md) + - [GetEdgeType](./GetEdgeType.md) + - [GetGraphResponse](./GetGraphResponse.md) + - [GetGraphSchemaResponse](./GetGraphSchemaResponse.md) + - [GetProcedureResponse](./GetProcedureResponse.md) + - [GetPropertyMeta](./GetPropertyMeta.md) + - [GetVertexType](./GetVertexType.md) + - [JobResponse](./JobResponse.md) + - [JobStatus](./JobStatus.md) + - [KeyValue](./KeyValue.md) + - [LongText](./LongText.md) + - [Parameter](./Parameter.md) + - [PrimitiveType](./PrimitiveType.md) + - [Property](./Property.md) + - [PropertyArray](./PropertyArray.md) + - [QueryRequest](./QueryRequest.md) + - [Record](./Record.md) + - [SchemaMapping](./SchemaMapping.md) + - [SchemaMappingLoadingConfig](./SchemaMappingLoadingConfig.md) + - [SchemaMappingLoadingConfigFormat](./SchemaMappingLoadingConfigFormat.md) + - [ServiceStatus](./ServiceStatus.md) + - [StartServiceRequest](./StartServiceRequest.md) + - [StoredProcedureMeta](./StoredProcedureMeta.md) + - [StringType](./StringType.md) + - [StringTypeString](./StringTypeString.md) + - [TemporalType](./TemporalType.md) + - [TimeStampType](./TimeStampType.md) + - [TypedValue](./TypedValue.md) + - [UpdateProcedureRequest](./UpdateProcedureRequest.md) + - [VarChar](./VarChar.md) + - [VarCharVarChar](./VarCharVarChar.md) + - [VertexData](./VertexData.md) + - [VertexMapping](./VertexMapping.md) + - [VertexRequest](./VertexRequest.md) + + + +## Documentation for Authorization + +Authentication is not supported yet, and we will be introducing authorization-related implementation in the near future. + + diff --git a/docs/flex/interactive/development/python/python_sdk.md b/docs/flex/interactive/development/python/python_sdk.md new file mode 100644 index 000000000000..9464da01d4ce --- /dev/null +++ b/docs/flex/interactive/development/python/python_sdk.md @@ -0,0 +1,175 @@ +# Python SDK Reference + +The Interactive Python SDK Reference is a comprehensive guide designed to assist developers in integrating the Interactive service into their Python applications. This SDK allows users to seamlessly connect to Interactive and harness its powerful features for graph management, stored procedure management, and query execution. + +## Requirements. + +Python 3.7+ + +## Installation & Usage +### pip install + +```bash +pip3 install graphscope-interactive +``` + +Then import the package: +```python +import interactive_sdk +``` + +### 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). + + +## Documentation for Service APIs + +All URIs are relative to `${INTERACTIVE_ENDPOINT}` + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*AdminServiceGraphManagementApi* | [**create_dataloading_job**](./AdminServiceGraphManagementApi.md#create_dataloading_job) | **POST** /v1/graph/{graph_id}/dataloading | +*AdminServiceGraphManagementApi* | [**create_graph**](./AdminServiceGraphManagementApi.md#create_graph) | **POST** /v1/graph | +*AdminServiceGraphManagementApi* | [**delete_graph**](./AdminServiceGraphManagementApi.md#delete_graph) | **DELETE** /v1/graph/{graph_id} | +*AdminServiceGraphManagementApi* | [**get_graph**](./AdminServiceGraphManagementApi.md#get_graph) | **GET** /v1/graph/{graph_id} | +*AdminServiceGraphManagementApi* | [**get_schema**](./AdminServiceGraphManagementApi.md#get_schema) | **GET** /v1/graph/{graph_id}/schema | +*AdminServiceGraphManagementApi* | [**list_graphs**](./AdminServiceGraphManagementApi.md#list_graphs) | **GET** /v1/graph | +*AdminServiceJobManagementApi* | [**delete_job_by_id**](./AdminServiceJobManagementApi.md#delete_job_by_id) | **DELETE** /v1/job/{job_id} | +*AdminServiceJobManagementApi* | [**get_job_by_id**](./AdminServiceJobManagementApi.md#get_job_by_id) | **GET** /v1/job/{job_id} | +*AdminServiceJobManagementApi* | [**list_jobs**](./AdminServiceJobManagementApi.md#list_jobs) | **GET** /v1/job | +*AdminServiceProcedureManagementApi* | [**create_procedure**](./AdminServiceProcedureManagementApi.md#create_procedure) | **POST** /v1/graph/{graph_id}/procedure | +*AdminServiceProcedureManagementApi* | [**delete_procedure**](./AdminServiceProcedureManagementApi.md#delete_procedure) | **DELETE** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceProcedureManagementApi* | [**get_procedure**](./AdminServiceProcedureManagementApi.md#get_procedure) | **GET** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceProcedureManagementApi* | [**list_procedures**](./AdminServiceProcedureManagementApi.md#list_procedures) | **GET** /v1/graph/{graph_id}/procedure | +*AdminServiceProcedureManagementApi* | [**update_procedure**](./AdminServiceProcedureManagementApi.md#update_procedure) | **PUT** /v1/graph/{graph_id}/procedure/{procedure_id} | +*AdminServiceServiceManagementApi* | [**get_service_status**](./AdminServiceServiceManagementApi.md#get_service_status) | **GET** /v1/service/status | +*AdminServiceServiceManagementApi* | [**restart_service**](./AdminServiceServiceManagementApi.md#restart_service) | **POST** /v1/service/restart | +*AdminServiceServiceManagementApi* | [**start_service**](./AdminServiceServiceManagementApi.md#start_service) | **POST** /v1/service/start | +*AdminServiceServiceManagementApi* | [**stop_service**](./AdminServiceServiceManagementApi.md#stop_service) | **POST** /v1/service/stop | +*GraphServiceEdgeManagementApi* | [**add_edge**](./GraphServiceEdgeManagementApi.md#add_edge) | **POST** /v1/graph/{graph_id}/edge | Add edge to the graph +*GraphServiceEdgeManagementApi* | [**delete_edge**](./GraphServiceEdgeManagementApi.md#delete_edge) | **DELETE** /v1/graph/{graph_id}/edge | Remove edge from the graph +*GraphServiceEdgeManagementApi* | [**get_edge**](./GraphServiceEdgeManagementApi.md#get_edge) | **GET** /v1/graph/{graph_id}/edge | Get the edge's properties with src and dst vertex primary keys. +*GraphServiceEdgeManagementApi* | [**update_edge**](./GraphServiceEdgeManagementApi.md#update_edge) | **PUT** /v1/graph/{graph_id}/edge | Update edge's property +*GraphServiceVertexManagementApi* | [**add_vertex**](./GraphServiceVertexManagementApi.md#add_vertex) | **POST** /v1/graph/{graph_id}/vertex | Add vertex to the graph +*GraphServiceVertexManagementApi* | [**delete_vertex**](./GraphServiceVertexManagementApi.md#delete_vertex) | **DELETE** /v1/graph/{graph_id}/vertex | Remove vertex from the graph +*GraphServiceVertexManagementApi* | [**get_vertex**](./GraphServiceVertexManagementApi.md#get_vertex) | **GET** /v1/graph/{graph_id}/vertex | Get the vertex's properties with vertex primary key. +*GraphServiceVertexManagementApi* | [**update_vertex**](./GraphServiceVertexManagementApi.md#update_vertex) | **PUT** /v1/graph/{graph_id}/vertex | Update vertex's property +*QueryServiceApi* | [**proc_call**](./QueryServiceApi.md#proc_call) | **POST** /v1/graph/{graph_id}/query | run queries on graph + + +## Documentation for Data Structures + + - [BaseEdgeType](./BaseEdgeType.md) + - [BaseEdgeTypeVertexTypePairRelationsInner](./BaseEdgeTypeVertexTypePairRelationsInner.md) + - [BaseEdgeTypeVertexTypePairRelationsInnerXCsrParams](./BaseEdgeTypeVertexTypePairRelationsInnerXCsrParams.md) + - [BasePropertyMeta](./BasePropertyMeta.md) + - [BaseVertexType](./BaseVertexType.md) + - [BaseVertexTypeXCsrParams](./BaseVertexTypeXCsrParams.md) + - [Collection](./Collection.md) + - [CollectiveResults](./CollectiveResults.md) + - [Column](./Column.md) + - [ColumnMapping](./ColumnMapping.md) + - [CreateEdgeType](./CreateEdgeType.md) + - [CreateGraphRequest](./CreateGraphRequest.md) + - [CreateGraphResponse](./CreateGraphResponse.md) + - [CreateGraphSchemaRequest](./CreateGraphSchemaRequest.md) + - [CreateProcedureRequest](./CreateProcedureRequest.md) + - [CreateProcedureResponse](./CreateProcedureResponse.md) + - [CreatePropertyMeta](./CreatePropertyMeta.md) + - [CreateVertexType](./CreateVertexType.md) + - [EdgeData](./EdgeData.md) + - [EdgeMapping](./EdgeMapping.md) + - [EdgeMappingDestinationVertexMappingsInner](./EdgeMappingDestinationVertexMappingsInner.md) + - [EdgeMappingSourceVertexMappingsInner](./EdgeMappingSourceVertexMappingsInner.md) + - [EdgeMappingSourceVertexMappingsInnerColumn](./EdgeMappingSourceVertexMappingsInnerColumn.md) + - [EdgeMappingTypeTriplet](./EdgeMappingTypeTriplet.md) + - [EdgeRequest](./EdgeRequest.md) + - [Element](./Element.md) + - [FixedChar](./FixedChar.md) + - [FixedCharChar](./FixedCharChar.md) + - [GSDataType](./GSDataType.md) + - [GetEdgeType](./GetEdgeType.md) + - [GetGraphResponse](./GetGraphResponse.md) + - [GetGraphSchemaResponse](./GetGraphSchemaResponse.md) + - [GetProcedureResponse](./GetProcedureResponse.md) + - [GetPropertyMeta](./GetPropertyMeta.md) + - [GetVertexType](./GetVertexType.md) + - [JobResponse](./JobResponse.md) + - [JobStatus](./JobStatus.md) + - [KeyValue](./KeyValue.md) + - [LongText](./LongText.md) + - [ModelProperty](./ModelProperty.md) + - [Parameter](./Parameter.md) + - [PrimitiveType](./PrimitiveType.md) + - [PropertyArray](./PropertyArray.md) + - [QueryRequest](./QueryRequest.md) + - [Record](./Record.md) + - [SchemaMapping](./SchemaMapping.md) + - [SchemaMappingLoadingConfig](./SchemaMappingLoadingConfig.md) + - [SchemaMappingLoadingConfigFormat](./SchemaMappingLoadingConfigFormat.md) + - [ServiceStatus](./ServiceStatus.md) + - [StartServiceRequest](./StartServiceRequest.md) + - [StoredProcedureMeta](./StoredProcedureMeta.md) + - [StringType](./StringType.md) + - [StringTypeString](./StringTypeString.md) + - [TemporalType](./TemporalType.md) + - [TimeStampType](./TimeStampType.md) + - [TypedValue](./TypedValue.md) + - [UpdateProcedureRequest](./UpdateProcedureRequest.md) + - [VarChar](./VarChar.md) + - [VarCharVarChar](./VarCharVarChar.md) + - [VertexData](./VertexData.md) + - [VertexMapping](./VertexMapping.md) + - [VertexRequest](./VertexRequest.md) + + + +## Documentation For Authorization + +Authentication is not supported yet, and we will be introducing authorization-related implementation in the near future. + + + diff --git a/docs/flex/interactive/development/admin_service.md b/docs/flex/interactive/development/restful_api.md similarity index 98% rename from docs/flex/interactive/development/admin_service.md rename to docs/flex/interactive/development/restful_api.md index 1e4eeab30372..43d71e9930d6 100644 --- a/docs/flex/interactive/development/admin_service.md +++ b/docs/flex/interactive/development/restful_api.md @@ -1,8 +1,8 @@ -# GraphScope Interactive Admin Service Documentation +# RESTful API Documentation ## Introduction -Welcome to the GraphScope Interactive Admin Service documentation. This guide is tailored for developers and administrators seeking to manage the Interactive service more efficiently. Here, we delve into the intricate workings of the RESTful HTTP interfaces provided by the Interactive Admin service, offering a comprehensive toolkit for real-time service management. This document is crucial for those looking to customize or enhance their GraphScope Interactive experience. +Welcome to the GraphScope Interactive RESTful API documentation. This guide is tailored for developers and administrators seeking to manage the Interactive service more efficiently. Here, we delve into the intricate workings of the RESTful HTTP interfaces provided by the Interactive Admin service, offering a comprehensive toolkit for real-time service management. This document is crucial for those looking to customize or enhance their GraphScope Interactive experience. ## API Overview diff --git a/docs/flex/interactive/sdk.md b/docs/flex/interactive/sdk.md new file mode 100644 index 000000000000..6c78439814cc --- /dev/null +++ b/docs/flex/interactive/sdk.md @@ -0,0 +1,12 @@ +# GraphScope Interactive SDK + +Interactive provides SDKs in two languages, Java and Python. Users can use the SDK in their preferred language to connect to the Interactive service, to manage graphs, manage stored procedures, and submit queries. + +```{toctree} arguments +--- +caption:GraphScope Interactive SDK +maxdepth: 3 +--- +sdk/java/java_sdk +sdk/python/python_sdk +``` \ No newline at end of file diff --git a/docs/flex/interactive/tools.md b/docs/flex/interactive/tools.md new file mode 100644 index 000000000000..08cf3316f236 --- /dev/null +++ b/docs/flex/interactive/tools.md @@ -0,0 +1,11 @@ +# Tools + +With the admin tool, users can use the Interactive service locally. With the Cypher-shell, users can interact with the Interactive service using the Cypher query language. + +```{toctree} arguments +--- +caption:GraphScope Interactive Tools +maxdepth: 3 +--- +tools/admin_tool +``` \ No newline at end of file diff --git a/docs/flex/interactive_intro.md b/docs/flex/interactive_intro.md index 720011c335c2..4b3dd61e50fc 100644 --- a/docs/flex/interactive_intro.md +++ b/docs/flex/interactive_intro.md @@ -20,4 +20,5 @@ interactive/data_import interactive/dev_guide interactive/glossary interactive/deployment +interactive/tools ``` \ No newline at end of file diff --git a/flex/interactive/sdk/README.md b/flex/interactive/sdk/README.md new file mode 100644 index 000000000000..8d9f33775575 --- /dev/null +++ b/flex/interactive/sdk/README.md @@ -0,0 +1,50 @@ +# GraphScope Interactive SDK + +GraphScope Interactive use [OpenAPI](https://openapis.org) to define the http interface, and use [OpenAPI-generator](https://openapi-generator.tech) to generate python/java SDKs. + +## Usage + +To use Interactive Python SDK +```bash +pip3 install interactive-sdk==0.0.3 +``` + +To use Interactive Java SDK +```xml + + com.alibaba.graphscope + interactive-java-sdk + 0.0.3 + +``` + +## Docs + +[GraphScope Interactive Java SDK](https://graphscope.io/docs/flex/interactive/java_sdk) +[GraphScope Interactive Python SDK](https://graphscope.io/docs/flex/interactive/python_sdk) + + +## Generate SDK by yourself + +The generated SDKs are not contained in the github codebase, you maybe interested in generated code. + +### Install OpenAPI-generator-cli + +```bash +mkdir -p ~/bin/openapitools +curl https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/bin/utils/openapi-generator-cli.sh > ~/bin/openapitools/openapi-generator-cli +chmod u+x ~/bin/openapitools/openapi-generator-cli +export PATH=$PATH:~/bin/openapitools/ +export OPENAPI_GENERATOR_VERSION=7.2.0 +``` + +### Generate SDKs + +```bash +# generate java sdk to java/interactive_sdk/ +bash generate_sdk.sh -g java +# generate python sdk to python/interactive_sdk/ +bash generate_sdk.sh -g python +``` + +## SDK examples. \ No newline at end of file diff --git a/flex/interactive/sdk/examples/java/interactive-sdk-example/pom.xml b/flex/interactive/sdk/examples/java/interactive-sdk-example/pom.xml new file mode 100644 index 000000000000..9f2008d7ff67 --- /dev/null +++ b/flex/interactive/sdk/examples/java/interactive-sdk-example/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + com.alibaba.graphscope + interactive-sdk-example + jar + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + interactive-sdk-example + http://maven.apache.org + + + com.alibaba.graphscope + interactive-java-sdk + 0.0.3 + + + diff --git a/flex/interactive/sdk/examples/java/interactive-sdk-example/src/main/java/com/alibaba/graphscope/BasicExample.java b/flex/interactive/sdk/examples/java/interactive-sdk-example/src/main/java/com/alibaba/graphscope/BasicExample.java new file mode 100644 index 000000000000..4ddd27ccb476 --- /dev/null +++ b/flex/interactive/sdk/examples/java/interactive-sdk-example/src/main/java/com/alibaba/graphscope/BasicExample.java @@ -0,0 +1,259 @@ +/* + * 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; + +import com.alibaba.graphscope.interactive.client.Driver; +import com.alibaba.graphscope.interactive.client.Session; +import com.alibaba.graphscope.interactive.client.common.Result; +import com.alibaba.graphscope.interactive.openapi.model.*; + +import org.apache.tinkerpop.gremlin.driver.Client; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Hello world! + */ +public class BasicExample { + + public static String createGraph(Session session) { + 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("")))))); + System.out.println("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); + if (rep.isOk()) { + System.out.println("create graph success"); + } else { + throw new RuntimeException("create graph failed: " + rep.getStatusMessage()); + } + String graphId = rep.getValue().getGraphId(); + System.out.println("graphId: " + graphId); + return graphId; + } + + public static String bulkLoading(Session session, String graphId) { + 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); + } + { + String personPath = + new File("../../../examples/modern_graph/person.csv").getAbsolutePath(); + String knowsPath = + new File("../../../examples/modern_graph/person_knows_person.csv") + .getAbsolutePath(); + { + 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); + if (rep.isOk()) { + System.out.println("bulk loading success"); + } else { + throw new RuntimeException("bulk loading failed: " + rep.getStatusMessage()); + } + String jobId = rep.getValue().getJobId(); + System.out.println("job id: " + jobId); + return jobId; + } + + public static void waitJobFinished(Session session, String jobId) { + if (jobId == null) { + return; + } + while (true) { + Result rep = session.getJobStatus(jobId); + if (!rep.isOk()) { + throw new RuntimeException("get job status failed: " + rep.getStatusMessage()); + } + JobStatus job = rep.getValue(); + if (job.getStatus() == JobStatus.StatusEnum.SUCCESS) { + System.out.println("job finished"); + break; + } else if (job.getStatus() == JobStatus.StatusEnum.FAILED) { + throw new RuntimeException("job failed"); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public static void main(String[] args) { + // get endpoint from command line + if (args.length != 1) { + System.out.println("Usage: java -jar "); + return; + } + String endpoint = args[0]; + Driver driver = Driver.connect(endpoint); + Session session = driver.session(); + + String graphId = createGraph(session); + String jobId = bulkLoading(session, graphId); + waitJobFinished(session, jobId); + System.out.println("bulk loading finished"); + + // start the service on the created graph + Result startServiceResponse = + session.startService(new StartServiceRequest().graphId(graphId)); + if (startServiceResponse.isOk()) { + System.out.println("start service success"); + } else { + throw new RuntimeException( + "start service failed: " + startServiceResponse.getStatusMessage()); + } + + // run cypher query + try (org.neo4j.driver.Session neo4jSession = driver.getNeo4jSession()) { + org.neo4j.driver.Result result = neo4jSession.run("MATCH(a) return COUNT(a);"); + System.out.println("result: " + result.toString()); + } + + // run gremlin query + Client gremlinClient = driver.getGremlinClient(); + try { + List results = + gremlinClient.submit("g.V().count()").all().get(); + System.out.println("result: " + results.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + + // Advanced features, creating a procedure + CreateProcedureRequest procedure = + new CreateProcedureRequest() + .name("testProcedure") + .description("a simple test procedure") + .query("MATCH(p:person) RETURN COUNT(p);") + .type(CreateProcedureRequest.TypeEnum.CYPHER); + Result resp = session.createProcedure(graphId, procedure); + if (resp.isOk()) { + System.out.println("create procedure success"); + } else { + throw new RuntimeException("create procedure failed: " + resp.getStatusMessage()); + } + + // restart the service to make the procedure take effect + Result restartResp = session.restartService(); + if (restartResp.isOk()) { + System.out.println("service restarted: " + restartResp.getValue()); + } else { + throw new RuntimeException("restart service failed: " + restartResp.getStatusMessage()); + } + // Sleep 5 seconds to wait for the service to restart + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // run the procedure + try (org.neo4j.driver.Session neo4jSession = driver.getNeo4jSession()) { + org.neo4j.driver.Result result = neo4jSession.run("CALL testProcedure() YIELD *;"); + System.out.println("result: " + result.toString()); + } + System.out.println("Finish all tests"); + return; + } +} diff --git a/flex/interactive/sdk/examples/python/basic_example.py b/flex/interactive/sdk/examples/python/basic_example.py new file mode 100644 index 000000000000..1a62d41ef6c9 --- /dev/null +++ b/flex/interactive/sdk/examples/python/basic_example.py @@ -0,0 +1,220 @@ +#!/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 time +import argparse +import os +from interactive_sdk.openapi.models.long_text import LongText +from interactive_sdk.openapi.models.edge_mapping_type_triplet import ( + EdgeMappingTypeTriplet, +) +from interactive_sdk.client.driver import Driver +from interactive_sdk.client.session import Session +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.gs_data_type import GSDataType +from interactive_sdk.openapi.models.start_service_request import StartServiceRequest +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.string_type import StringType +from interactive_sdk.openapi.models.string_type_string import StringTypeString +from interactive_sdk.openapi.models.vertex_mapping import VertexMapping + + +def createGraph(sess: Session): + 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 = sess.create_graph(create_graph) + assert resp.is_ok() + graph_id = resp.get_value().graph_id + print("create graph: ", graph_id) + return graph_id + + +def bulkLoading(sess: Session, graph_id: str): + person_csv_path = os.path.abspath("../../../examples/modern_graph/person.csv") + knows_csv_path = os.path.abspath( + "../../../examples/modern_graph/person_knows_person.csv" + ) + schema_mapping = SchemaMapping( + graph=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 = sess.bulk_loading(graph_id, schema_mapping) + assert resp.is_ok() + job_id = resp.get_value().job_id + return job_id + + +def waitJobFinish(sess: Session, job_id: str): + while True: + resp = sess.get_job(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) + + +if __name__ == "__main__": + # expect one argument: interactive_endpoint + parser = argparse.ArgumentParser(description="Example Python3 script") + + # Add arguments + parser.add_argument( + "--endpoint", + type=str, + help="The interactive endpoint to connect", + required=True, + default="https://virtserver.swaggerhub.com/GRAPHSCOPE/interactive/1.0.0/", + ) + + # Parse the arguments + args = parser.parse_args() + + driver = Driver(endpoint=args.endpoint) + with driver.session() as sess: + graph_id = createGraph(sess) + job_id = bulkLoading(sess, graph_id) + waitJobFinish(sess, job_id) + print("bulk loading finished") + + # Now start service on the created graph. + resp = sess.start_service( + start_service_request=StartServiceRequest(graph_id=graph_id) + ) + assert resp.is_ok() + time.sleep(5) + print("restart service on graph ", graph_id) + + # running a simple cypher query + query = "MATCH (n) RETURN COUNT(n);" + with driver.getNeo4jSession() as session: + resp = session.run(query) + for record in resp: + print(record) + + # running a simple gremlin query + query = "g.V().count();" + ret = [] + gremlin_client = driver.getGremlinClient() + q = gremlin_client.submit(query) + while True: + try: + ret.extend(q.next()) + except StopIteration: + break + print(ret) + + # more advanced usage of procedure + create_proc_request = CreateProcedureRequest( + name="test_procedure", + description="test procedure", + query="MATCH (n) RETURN COUNT(n);", + type="cypher", + ) + resp = sess.create_procedure(graph_id, create_proc_request) + assert resp.is_ok() + + # must start service on the current graph, to let the procedure take effect + resp = sess.restart_service() + assert resp.is_ok() + print("restarted service on graph ", graph_id) + time.sleep(5) + + # Now call the procedure + with driver.getNeo4jSession() as session: + result = session.run("CALL test_procedure();") + for record in result: + print(record) diff --git a/flex/interactive/sdk/generate_sdk.sh b/flex/interactive/sdk/generate_sdk.sh new file mode 100755 index 000000000000..48b96885f2fe --- /dev/null +++ b/flex/interactive/sdk/generate_sdk.sh @@ -0,0 +1,146 @@ +#!/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. + +# This script is used to generate the Java SDK from the Flex Interactive API +# It uses the Swagger Codegen tool to generate the SDK + +PACKAGE_NAME="com.alibaba.graphscope.interactive.openapi" +PYTHON_PACKAGE_NAME="openapi" +GROUP_ID="com.alibaba.graphscope" +ARTIFACT_ID="interactive-java-sdk" +ARTIFACT_URL="https://github.com/alibaba/GraphScope/tree/main/flex/interactive" +VERSION="0.0.3" +EMAIL="graphscope@alibaba-inc.com" +DESCRIPTION="GraphScope Interactive Java SDK" +ORGANIZATION="Alibaba GraphScope" +developerOrganizationUrl="https://graphscope.io" +DEVELOPER_NAME="GraphScope Team" +LICENSE_NAME="Apache-2.0" +LICENSE_URL="https://www.apache.org/licenses/LICENSE-2.0.html" + + +#get current bash scrip's directory +CUR_DIR=$(cd `dirname $0`; pwd) +OPENAPI_SPEC_PATH="${CUR_DIR}/../../openapi/openapi_interactive.yaml" + + +function usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " -h, --help Show this help message and exit" + echo " -g, --lang Generate the SDK for the specified language" + echo " Supported languages: java/python" +} + +function do_gen_java() { + echo "Generating Java SDK" + OUTPUT_PATH="${CUR_DIR}/java/" + + + addtional_properties="licenseName=${LICENSE_NAME},licenseUrl=${LICENSE_URL},artifactUrl=${ARTIFACT_URL}" + addtional_properties="${addtional_properties},artifactDescription=\"${DESCRIPTION}\",developerEmail=${EMAIL}" + addtional_properties="${addtional_properties},developerName=\"${DEVELOPER_NAME}\",developerOrganization=\"${ORGANIZATION}\"" + addtional_properties="${addtional_properties},developerOrganizationUrl=${developerOrganizationUrl}" + addtional_properties="${addtional_properties},artifactVersion=${VERSION},hideGenerationTimestamp=true" + + cmd="openapi-generator-cli generate -i ${OPENAPI_SPEC_PATH} -g java -o ${OUTPUT_PATH}" + cmd=" ${cmd} --api-package ${PACKAGE_NAME}.api" + cmd=" ${cmd} --model-package ${PACKAGE_NAME}.model" + cmd=" ${cmd} --invoker-package ${PACKAGE_NAME}" + cmd=" ${cmd} --package-name ${PACKAGE_NAME}" + cmd=" ${cmd} --artifact-id ${ARTIFACT_ID}" + cmd=" ${cmd} --group-id ${GROUP_ID}" + cmd=" ${cmd} --additional-properties=${addtional_properties}" + echo "Running command: ${cmd}" + + eval $cmd +} + +function do_gen_python() { + echo "Generating Python SDK" + OUTPUT_PATH="${CUR_DIR}/python" + cmd="openapi-generator-cli generate -i ${OPENAPI_SPEC_PATH} -g python -o ${OUTPUT_PATH}" + cmd=" ${cmd} --invoker-package ${PYTHON_PACKAGE_NAME}" + cmd=" ${cmd} --package-name interactive_sdk.openapi" + cmd=" ${cmd} --additional-properties=packageVersion=${VERSION},pythonVersion=3" + echo "Running command: ${cmd}" + eval $cmd +} + +function do_gen() { + # expect only one argument + if [ $# -ne 1 ]; then + err "Invalid number of arguments:$#" + usage + exit 1 + fi + # to lower case + lang=$(echo $1 | tr '[:upper:]' '[:lower:]') + case $lang in + java) + do_gen_java + ;; + python) + do_gen_python + ;; + *) + err "Unsupported language: $lang" + usage + exit 1 + ;; + esac +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +function install_generator() { + # first check openapi-generator-cli is executable + if command -v openapi-generator-cli &>/dev/null; then + echo "openapi-generator-cli is already installed" + return + fi + mkdir -p ~/bin/openapitools + curl https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/bin/utils/openapi-generator-cli.sh > ~/bin/openapitools/openapi-generator-cli + chmod u+x ~/bin/openapitools/openapi-generator-cli + export PATH=$PATH:~/bin/openapitools/ + export OPENAPI_GENERATOR_VERSION=7.2.0 + sudo apt-get update && sudo apt-get -y install jq +} + +install_generator + +while [[ $# -gt 0 ]]; do + key="$1" + + case $key in + -h | --help) + usage + exit + ;; + -g | --lang) + shift + do_gen "$@" + exit 0 + ;; + *) # unknown option + err "unknown option $1" + usage + exit 1 + ;; + esac +done \ No newline at end of file diff --git a/flex/interactive/sdk/java/.openapi-generator-ignore b/flex/interactive/sdk/java/.openapi-generator-ignore new file mode 100644 index 000000000000..d9b2078b4be9 --- /dev/null +++ b/flex/interactive/sdk/java/.openapi-generator-ignore @@ -0,0 +1,28 @@ +# 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 +pom.xml +src/test/ +src/main/java/com/alibaba/graphscope/interactive/client diff --git a/flex/interactive/sdk/java/README.md b/flex/interactive/sdk/java/README.md new file mode 100644 index 000000000000..f5643762ef71 --- /dev/null +++ b/flex/interactive/sdk/java/README.md @@ -0,0 +1,96 @@ +# Java SDK Reference + +The Interactive Java SDK Reference is a comprehensive guide for developers looking to integrate the Interactive service into their Java applications. This SDK allows users to seamlessly connect to Interactive and leverage its powerful features for graph management, stored procedure management, and query execution. + + +## Requirements + +Building the API client library requires: +1. Java 1.8+ +2. Maven (3.8.3+)/Gradle (7.2+) + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +git clone https://github.com/alibaba/GraphScope.git +cd GraphScope/flex/interactive/sdk/java +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + com.alibaba.graphscope + interactive-java-sdk + 0.0.3 + compile + +``` + +### Others + +At first generate the JAR by executing: + +```shell +mvn clean package +``` + +Then manually install the following JARs: + +* `target/interactive-java-sdk-0.0.3.jar` +* `target/lib/*.jar` + +## 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. + +```java +package com.alibaba.graphscope; + +import com.alibaba.graphscope.interactive.client.Driver; +import com.alibaba.graphscope.interactive.client.Session; + +public class GettingStarted { + public static void main(String[] args) { + //get endpoint from command line + if (args.length != 1) { + System.out.println("Usage: "); + return; + } + String endpoint = args[0]; + Driver driver = Driver.connect(endpoint); + Session session = driver.session(); + + // start a query + // run cypher query + try (org.neo4j.driver.Session neo4jSession = driver.getNeo4jSession()) { + org.neo4j.driver.Result result = neo4jSession.run("MATCH(a) return COUNT(a);"); + System.out.println("result: " + result.toString()); + } + return; + } +} +``` + +For more a more detailed example, please refer to [Java SDK Example](https://github.com/alibaba/GraphScope/flex/interactive/sdk/examples/java/interactive-sdk-example/) + + diff --git a/flex/interactive/sdk/java/api/openapi.yaml b/flex/interactive/sdk/java/api/openapi.yaml new file mode 100644 index 000000000000..382eb69e9fc5 --- /dev/null +++ b/flex/interactive/sdk/java/api/openapi.yaml @@ -0,0 +1,2172 @@ +openapi: 3.0.3 +info: + contact: + email: graphscope@alibaba-inc.com + 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. + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: GraphScope Interactive API v0.0.3 + version: 1.0.0 +externalDocs: + description: Find out More about GraphScope + url: http://graphscope.io +servers: +- 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 +tags: +- description: GraphManagement + name: AdminService/GraphManagement +- description: ProcedureManagement + name: AdminService/ProcedureManagement +- description: ServiceManagement + name: AdminService/ServiceManagement +- description: VertexManagement + name: GraphService/VertexManagement +- description: EdgeManagement + name: GraphService/EdgeManagement +- description: Graph query + name: QueryService +paths: + /v1/graph: + get: + description: List all graphs + operationId: list_graphs + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/GetGraphResponse' + type: array + description: Successful operation + tags: + - AdminService/GraphManagement + x-accepts: application/json + post: + description: Create a new graph + operationId: create_graph + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateGraphRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/CreateGraphResponse' + description: successful operation + "400": + content: + application/json: + schema: + type: string + description: BadRequest + "500": + content: + application/json: + schema: + type: string + description: Internal error + tags: + - AdminService/GraphManagement + x-content-type: application/json + x-accepts: application/json + /v1/graph/{graph_id}: + delete: + description: Delete a graph by name + operationId: delete_graph + parameters: + - description: The name of graph to delete + explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + example: Successfully delete graph + schema: + $ref: '#/components/schemas/APIResponse' + description: Successful operation + "404": + content: + application/json: + example: Graph not found + schema: + $ref: '#/components/schemas/APIResponse' + description: Not Found + "500": + content: + application/json: + example: "Internal Error: " + schema: + $ref: '#/components/schemas/APIResponse' + description: Internal Error + tags: + - AdminService/GraphManagement + x-accepts: application/json + get: + description: Get a graph by name + operationId: get_graph + parameters: + - description: The name of graph to get + explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/GetGraphResponse' + description: Successful operation + "404": + content: + application/json: + schema: + example: graph not exists + type: string + description: Not found + tags: + - AdminService/GraphManagement + x-accepts: application/json + /v1/graph/{graph_id}/schema: + get: + description: Get schema by graph name + operationId: get_schema + parameters: + - description: The name of graph to delete + explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/GetGraphSchemaResponse' + description: successful operation + tags: + - AdminService/GraphManagement + x-accepts: application/json + /v1/graph/{graph_id}/dataloading: + post: + description: Create a dataloading job + operationId: create_dataloading_job + parameters: + - description: The name of graph to do bulk loading. + explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SchemaMapping' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobResponse' + description: successful operation + tags: + - AdminService/GraphManagement + x-content-type: application/json + x-accepts: application/json + /v1/job/{job_id}: + delete: + operationId: delete_job_by_id + parameters: + - explode: false + in: path + name: job_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + example: "Successfully cancel job: 123" + schema: + $ref: '#/components/schemas/APIResponse' + description: Successful operation + tags: + - AdminService/JobManagement + x-accepts: application/json + get: + operationId: get_job_by_id + parameters: + - description: "The id of the job, returned from POST /v1/graph/{graph_id}/dataloading" + explode: false + in: path + name: job_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobStatus' + description: successful operation + tags: + - AdminService/JobManagement + x-accepts: application/json + /v1/job: + get: + operationId: list_jobs + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/JobStatus' + type: array + description: successful operation + tags: + - AdminService/JobManagement + x-accepts: application/json + /v1/graph/{graph_id}/procedure: + get: + description: List all procedures + operationId: list_procedures + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/GetProcedureResponse' + type: array + description: Successful operation + "404": + content: + application/json: + schema: + example: Graph not found + type: string + description: Not found + tags: + - AdminService/ProcedureManagement + x-accepts: application/json + post: + description: Create a new procedure on a graph + operationId: create_procedure + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateProcedureRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/CreateProcedureResponse' + description: successful operation + "400": + content: + application/json: + example: Bad request + schema: + $ref: '#/components/schemas/APIResponse' + description: Bad request + "404": + content: + application/json: + example: Graph not found + schema: + $ref: '#/components/schemas/APIResponse' + description: not found + "500": + content: + application/json: + example: Internal error + schema: + $ref: '#/components/schemas/APIResponse' + description: Internal Error + tags: + - AdminService/ProcedureManagement + x-content-type: application/json + x-accepts: application/json + /v1/graph/{graph_id}/procedure/{procedure_id}: + delete: + description: Delete a procedure on a graph by name + operationId: delete_procedure + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - explode: false + in: path + name: procedure_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + description: Successful operation + "404": + content: + application/json: + schema: + example: Graph Not found/Procedure Not found + type: string + description: Not Found + tags: + - AdminService/ProcedureManagement + x-accepts: application/json + get: + description: Get a procedure by name + operationId: get_procedure + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - explode: false + in: path + name: procedure_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/GetProcedureResponse' + description: successful operation + "404": + content: + application/json: + schema: + example: Graph not found/procedure not found + type: string + description: Not found + tags: + - AdminService/ProcedureManagement + x-accepts: application/json + put: + description: Update procedure on a graph by name + operationId: update_procedure + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - explode: false + in: path + name: procedure_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProcedureRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + description: Successful operation + "400": + content: + application/json: + schema: + example: bad request + type: string + description: Bad request + "404": + content: + application/json: + schema: + example: Graph Not found/Procedure Not found + type: string + description: Not Found + "500": + content: + application/json: + schema: + example: bad request + type: string + description: Internal error + tags: + - AdminService/ProcedureManagement + x-content-type: application/json + x-accepts: application/json + /v1/service/start: + post: + description: Start service on a specified graph + operationId: start_service + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StartServiceRequest' + description: Start service on a specified graph + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + description: successful operation + "500": + content: + application/json: + schema: + example: Internal Error + type: string + description: Internal Error + tags: + - AdminService/ServiceManagement + x-content-type: application/json + x-accepts: application/json + /v1/service/stop: + post: + description: Stop current service + operationId: stop_service + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + description: successful operation + tags: + - AdminService/ServiceManagement + x-accepts: application/json + /v1/service/restart: + post: + description: Start current service + operationId: restart_service + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + description: successful operation + tags: + - AdminService/ServiceManagement + x-accepts: application/json + /v1/service/status: + get: + description: Get service status + operationId: get_service_status + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceStatus' + description: successful operation + tags: + - AdminService/ServiceManagement + x-accepts: application/json + /v1/graph/{graph_id}/vertex: + delete: + description: | + Remove the vertex from the specified graph. + operationId: delete_vertex + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - description: The label name of querying vertex. + explode: true + in: query + name: label + required: true + schema: + type: string + style: form + - description: The value of the querying vertex's primary key + explode: true + in: query + name: primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + responses: + "200": + content: + application/json: + example: + message: Successfully delete vertex + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully delete vertex + "400": + description: Invalid input vertex + "404": + description: Vertex not exists or Graph not exits. + "500": + description: Server internal error + summary: Remove vertex from the graph + tags: + - GraphService/VertexManagement + x-accepts: application/json + get: + description: | + Get the properties for the specified vertex. + example: + ```http + GET /endpoint?param1=value1¶m2=value2 HTTP/1.1 + Host: example.com + ``` + operationId: get_vertex + parameters: + - description: The name of the graph + explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - description: The label name of querying vertex. + explode: true + in: query + name: label + required: true + schema: + type: string + style: form + - description: The primary key value of querying vertex. + explode: true + in: query + name: primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + responses: + "200": + content: + application/json: + example: + label: person + values: + - name: id + value: 1 + - name: age + value: 23 + - name: name + value: amy + schema: + $ref: '#/components/schemas/VertexData' + description: Found vertex + "400": + description: Bad input parameter + "404": + description: Vertex not found or graph not found + "500": + description: Server internal error + summary: Get the vertex's properties with vertex primary key. + tags: + - GraphService/VertexManagement + x-accepts: application/json + post: + description: | + Add the provided vertex to the specified graph. + operationId: add_vertex + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + example: + label: person + primimary_key_value: 1 + properties: + age: 2 + name: Bob + schema: + $ref: '#/components/schemas/VertexRequest' + description: Add vertex to graph. + responses: + "200": + content: + application/json: + example: + message: Successfully created vertex + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully created vertex + "400": + description: Invalid input vertex + "404": + description: Graph not found + "409": + description: Vertex already exists + "500": + description: Server internal error + summary: Add vertex to the graph + tags: + - GraphService/VertexManagement + x-content-type: application/json + x-accepts: application/json + put: + description: | + Remove the vertex from the specified graph. + operationId: update_vertex + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + example: + label: person + primary_key_value: 2 + properties: + age: 24 + name: Cindy + schema: + $ref: '#/components/schemas/VertexRequest' + responses: + "200": + content: + application/json: + example: + message: Successfully updated vertex + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully update vertex + "400": + description: Invalid input paramters + "404": + description: Vertex not exists + "500": + description: Server internal error + summary: Update vertex's property + tags: + - GraphService/VertexManagement + x-content-type: application/json + x-accepts: application/json + /v1/graph/{graph_id}/edge: + delete: + description: | + Remove the edge from current graph. + operationId: delete_edge + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - description: The label name of src vertex. + example: person + explode: true + in: query + name: src_label + required: true + schema: + type: string + style: form + - description: The primary key value of src vertex. + example: 1 + explode: true + in: query + name: src_primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + - description: The label name of dst vertex. + example: software + explode: true + in: query + name: dst_label + required: true + schema: + type: string + style: form + - description: The primary key value of dst vertex. + example: 3 + explode: true + in: query + name: dst_primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + responses: + "200": + content: + application/json: + example: + message: Successfully delete edge + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully delete edge + "400": + description: Invalid input edge + "404": + description: Edge not exists or Graph not exits + "500": + description: Server internal error + summary: Remove edge from the graph + tags: + - GraphService/EdgeManagement + x-accepts: application/json + get: + description: | + Get the properties for the specified vertex. + operationId: get_edge + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + - description: The label name of querying edge. + example: created + explode: true + in: query + name: edge_label + required: true + schema: + type: string + style: form + - description: The label name of src vertex. + example: person + explode: true + in: query + name: src_label + required: true + schema: + type: string + style: form + - description: The primary key value of src vertex. + example: 1 + explode: true + in: query + name: src_primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + - description: The label name of dst vertex. + example: software + explode: true + in: query + name: dst_label + required: true + schema: + type: string + style: form + - description: The value of dst vertex's primary key + example: 3 + explode: true + in: query + name: dst_primary_key_value + required: true + schema: + $ref: '#/components/schemas/AnyValue' + style: form + responses: + "200": + content: + application/json: + example: + src_label: person + dst_label: software + edge_label: created + src_pk_value: 1 + dst_pk_value: 3 + properties: + - name: weight + value: 0.2 + schema: + $ref: '#/components/schemas/EdgeData' + description: Found Edge + "400": + description: Bad input parameter + "404": + description: Edge not found or Graph not found + "500": + description: Server internal error + summary: Get the edge's properties with src and dst vertex primary keys. + tags: + - GraphService/EdgeManagement + x-accepts: application/json + post: + description: | + Add the edge to graph. + operationId: add_edge + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + 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 + schema: + $ref: '#/components/schemas/EdgeRequest' + responses: + "200": + content: + application/json: + example: + message: Successfuly create edge + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully insert the edge + "400": + description: Invalid input edge + "409": + description: edge already exists + "500": + description: Server internal error + summary: Add edge to the graph + tags: + - GraphService/EdgeManagement + x-content-type: application/json + x-accepts: application/json + put: + description: | + Update the edge on the running graph. + operationId: update_edge + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + 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 + schema: + $ref: '#/components/schemas/EdgeRequest' + responses: + "200": + content: + application/json: + example: + message: Successfully update edge + schema: + $ref: '#/components/schemas/APIResponse' + description: Successfully update edge + "400": + description: Invalid input paramters + "404": + description: Edge not exists + "500": + description: Server internal error + summary: Update edge's property + tags: + - GraphService/EdgeManagement + x-content-type: application/json + x-accepts: application/json + /v1/graph/{graph_id}/query: + post: + 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. + operationId: proc_call + parameters: + - explode: false + in: path + name: graph_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/CollectiveResults' + description: Successfully runned. Empty if failed? + "500": + description: Server internal error + summary: run queries on graph + tags: + - QueryService + x-content-type: application/json + x-accepts: application/json +components: + schemas: + AnyValue: {} + TypedValue: + example: + type: null + value: "" + properties: + type: + $ref: '#/components/schemas/GSDataType' + value: {} + required: + - type + - value + type: object + Element: + oneOf: + - $ref: '#/components/schemas/TypedValue' + Collection: + properties: + values: + items: + $ref: '#/components/schemas/Element' + type: array + type: object + KeyValue: + properties: + key: + $ref: '#/components/schemas/TypedValue' + value: + $ref: '#/components/schemas/Element' + required: + - key + - value + type: object + Column: + oneOf: + - $ref: '#/components/schemas/Element' + - $ref: '#/components/schemas/Collection' + - $ref: '#/components/schemas/KeyValue' + Record: + example: + columns: + - null + - null + properties: + columns: + items: + $ref: '#/components/schemas/Column' + type: array + type: object + CollectiveResults: + example: + records: + - columns: + - null + - null + - columns: + - null + - null + properties: + records: + items: + $ref: '#/components/schemas/Record' + type: array + type: object + PrimitiveType: + properties: + primitive_type: + enum: + - DT_SIGNED_INT32 + - DT_UNSIGNED_INT32 + - DT_SIGNED_INT64 + - DT_UNSIGNED_INT64 + - DT_BOOL + - DT_FLOAT + - DT_DOUBLE + example: DT_SIGNED_INT32 + type: string + required: + - primitive_type + type: object + x-body-name: primitive_type + LongText: + properties: + long_text: + nullable: true + type: string + required: + - long_text + type: object + x-body-name: long_text + FixedChar: + properties: + char: + $ref: '#/components/schemas/FixedChar_char' + required: + - char + type: object + x-body-name: fixed_char + VarChar: + properties: + var_char: + $ref: '#/components/schemas/VarChar_var_char' + required: + - var_char + type: object + x-body-name: var_char + StringType: + properties: + string: + $ref: '#/components/schemas/StringType_string' + required: + - string + type: object + x-body-name: string_type + TimeStampType: + properties: + timestamp: + type: string + required: + - timestamp + type: object + x-body-name: time_stamp_type + TemporalType: + oneOf: + - $ref: '#/components/schemas/TimeStampType' + type: object + x-body-name: temporal_type + GSDataType: + oneOf: + - $ref: '#/components/schemas/PrimitiveType' + - $ref: '#/components/schemas/StringType' + - $ref: '#/components/schemas/TemporalType' + x-body-name: gs_data_type + Property: + example: + name: id + value: "" + properties: + name: + example: id + type: string + value: {} + required: + - name + - value + type: object + x-body-name: property + PropertyArray: + example: + properties: + - name: id + value: "" + - name: id + value: "" + properties: + properties: + items: + $ref: '#/components/schemas/Property' + type: array + required: + - properties + type: object + x-body-name: property_array + Parameter: + properties: + name: + example: param1 + type: string + type: + $ref: '#/components/schemas/GSDataType' + required: + - name + - type + type: object + x-body-name: parameter + VertexRequest: + example: + label: person + primary_key_value: "" + properties: + properties: + - name: id + value: "" + - name: id + value: "" + properties: + label: + example: person + type: string + primary_key_value: {} + properties: + $ref: '#/components/schemas/PropertyArray' + required: + - label + - primary_key_value + type: object + x-body-name: vertex_request + VertexData: + example: + values: + - name: id + value: "" + - name: id + value: "" + label: person + properties: + label: + example: person + type: string + values: + items: + $ref: '#/components/schemas/Property' + type: array + required: + - label + type: object + x-body-name: vertex_data + EdgeData: + example: + src_label: person + src_primary_key_value: "" + dst_label: software + edge_label: created + dst_primary_key_value: "" + properties: + - name: id + value: "" + - name: id + value: "" + properties: + src_label: + example: person + type: string + dst_label: + example: software + type: string + edge_label: + example: created + type: string + src_primary_key_value: {} + dst_primary_key_value: {} + properties: + items: + $ref: '#/components/schemas/Property' + type: array + required: + - dst_label + - dst_primary_key_value + - edge_label + - properties + - src_label + - src_primary_key_value + type: object + x-body-name: edge_data + EdgeRequest: + example: + src_label: person + src_primary_key_value: "" + dst_label: software + edge_label: created + dst_primary_key_value: "" + properties: + - name: id + value: "" + - name: id + value: "" + properties: + src_label: + example: person + type: string + dst_label: + example: software + type: string + edge_label: + example: created + type: string + src_primary_key_value: {} + dst_primary_key_value: {} + properties: + items: + $ref: '#/components/schemas/Property' + type: array + required: + - dst_label + - dst_primary_key_value + - edge_label + - src_label + - src_primary_key_value + type: object + x-body-name: edge_request + QueryRequest: + example: + query_name: ic1 + arguments: + - type: null + value: "" + - type: null + value: "" + properties: + query_name: + example: ic1 + type: string + arguments: + items: + $ref: '#/components/schemas/TypedValue' + type: array + required: + - query_name + type: object + x-body-name: query_request + CreateProcedureRequest: + example: + query: MATCH(a) return COUNT(a); + name: query1 + description: A sample stored procedure + type: cpp + properties: + name: + example: query1 + type: string + description: + example: A sample stored procedure + type: string + type: + enum: + - cpp + - cypher + type: string + query: + example: MATCH(a) return COUNT(a); + type: string + required: + - name + - query + - type + type: object + x-body-name: create_procedure_request + CreateProcedureResponse: + example: + procedure_id: proc1 + properties: + procedure_id: + example: proc1 + type: string + required: + - procedure_id + type: object + x-body-name: create_procedure_response + StoredProcedureMeta: + allOf: + - $ref: '#/components/schemas/CreateProcedureRequest' + - properties: + id: + example: "The unique identifier of procedure, currently is same with name." + type: string + library: + example: /path/to/library + type: string + params: + items: + $ref: '#/components/schemas/Parameter' + type: array + returns: + items: + $ref: '#/components/schemas/Parameter' + type: array + enable: + example: true + type: boolean + type: object + x-body-name: stored_procedure_meta + GetProcedureResponse: + allOf: + - $ref: '#/components/schemas/StoredProcedureMeta' + - properties: + bound_graph: + type: string + runnable: + type: boolean + creation_time: + type: integer + type: object + x-body-name: get_procedure_response + UpdateProcedureRequest: + example: + description: A sample stored procedure + properties: + description: + example: A sample stored procedure + type: string + type: object + x-body-name: update_procedure_request + CreateGraphResponse: + example: + graph_id: "1" + properties: + graph_id: + example: "1" + type: string + type: object + x-body-name: create_graph_response + CreateGraphRequest: + example: + schema: + vertex_types: + - null + - null + edge_types: + - null + - null + stored_procedures: + - query: MATCH(a) return COUNT(a); + name: query1 + description: A sample stored procedure + type: cpp + - query: MATCH(a) return COUNT(a); + name: query1 + description: A sample stored procedure + type: cpp + name: modern_graph + description: A default description + properties: + name: + example: modern_graph + type: string + description: + example: A default description + type: string + stored_procedures: + items: + $ref: '#/components/schemas/CreateProcedureRequest' + type: array + schema: + $ref: '#/components/schemas/CreateGraphSchemaRequest' + type: object + x-body-name: create_graph_request + APIResponse: + example: Response string + type: string + x-body-name: api_response + GetGraphResponse: + example: + creation_time: 11223444 + schema: + vertex_types: + - null + - null + edge_types: + - null + - null + stored_procedures: + - null + - null + name: name + description: description + id: id + store_type: mutable_csr + data_import_config: + loading_config: + format: + metadata: + key: "" + type: type + import_option: init + edge_mappings: + - inputs: + - inputs + - inputs + source_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + destination_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + type_triplet: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + - inputs: + - inputs + - inputs + source_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + destination_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + type_triplet: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + graph: graph + vertex_mappings: + - type_name: type_name + inputs: + - file:///path/to/person.csv + - file:///path/to/person.csv + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + - type_name: type_name + inputs: + - file:///path/to/person.csv + - file:///path/to/person.csv + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + data_update_time: 11123445 + properties: + id: + type: string + name: + type: string + description: + type: string + store_type: + enum: + - mutable_csr + type: string + creation_time: + example: 11223444 + type: integer + data_update_time: + example: 11123445 + type: integer + stored_procedures: + items: + $ref: '#/components/schemas/GetProcedureResponse' + type: array + schema: + $ref: '#/components/schemas/GetGraphSchemaResponse' + data_import_config: + $ref: '#/components/schemas/SchemaMapping' + type: object + x-body-name: get_graph_response + CreateGraphSchemaRequest: + example: + vertex_types: + - null + - null + edge_types: + - null + - null + properties: + vertex_types: + items: + $ref: '#/components/schemas/CreateVertexType' + type: array + edge_types: + items: + $ref: '#/components/schemas/CreateEdgeType' + type: array + type: object + x-body-name: create_graph_schema_request + GetGraphSchemaResponse: + example: + vertex_types: + - null + - null + edge_types: + - null + - null + properties: + vertex_types: + items: + $ref: '#/components/schemas/GetVertexType' + type: array + edge_types: + items: + $ref: '#/components/schemas/GetEdgeType' + type: array + type: object + x-body-name: get_graph_schema_response + BaseVertexType: + properties: + type_name: + type: string + primary_keys: + items: + type: string + type: array + x_csr_params: + $ref: '#/components/schemas/BaseVertexType_x_csr_params' + type: object + CreateVertexType: + allOf: + - $ref: '#/components/schemas/BaseVertexType' + - properties: + properties: + items: + $ref: '#/components/schemas/CreatePropertyMeta' + type: array + type: object + x-body-name: create_vertex_type + GetVertexType: + allOf: + - $ref: '#/components/schemas/BaseVertexType' + - properties: + type_id: + format: int32 + type: integer + properties: + items: + $ref: '#/components/schemas/GetPropertyMeta' + type: array + description: + type: string + type: object + x-body-name: get_vertex_type + BaseEdgeType: + properties: + type_name: + type: string + vertex_type_pair_relations: + items: + $ref: '#/components/schemas/BaseEdgeType_vertex_type_pair_relations_inner' + type: array + type: object + CreateEdgeType: + allOf: + - $ref: '#/components/schemas/BaseEdgeType' + - properties: + properties: + items: + $ref: '#/components/schemas/CreatePropertyMeta' + type: array + type: object + x-body-name: create_edge_type + GetEdgeType: + allOf: + - $ref: '#/components/schemas/BaseEdgeType' + - properties: + type_id: + format: int32 + type: integer + description: + type: string + properties: + items: + $ref: '#/components/schemas/GetPropertyMeta' + type: array + type: object + x-body-name: get_edge_type + BasePropertyMeta: + properties: + property_name: + type: string + property_type: + $ref: '#/components/schemas/GSDataType' + type: object + CreatePropertyMeta: + allOf: + - $ref: '#/components/schemas/BasePropertyMeta' + x-body-name: create_property_meta + GetPropertyMeta: + allOf: + - $ref: '#/components/schemas/BasePropertyMeta' + - properties: + property_id: + format: int32 + type: integer + type: object + x-body-name: get_property_meta + SchemaMapping: + example: + loading_config: + format: + metadata: + key: "" + type: type + import_option: init + edge_mappings: + - inputs: + - inputs + - inputs + source_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + destination_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + type_triplet: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + - inputs: + - inputs + - inputs + source_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + destination_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + type_triplet: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + graph: graph + vertex_mappings: + - type_name: type_name + inputs: + - file:///path/to/person.csv + - file:///path/to/person.csv + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + - type_name: type_name + inputs: + - file:///path/to/person.csv + - file:///path/to/person.csv + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + properties: + graph: + type: string + loading_config: + $ref: '#/components/schemas/SchemaMapping_loading_config' + vertex_mappings: + items: + $ref: '#/components/schemas/VertexMapping' + type: array + edge_mappings: + items: + $ref: '#/components/schemas/EdgeMapping' + type: array + type: object + x-body-name: schema_mapping + VertexMapping: + example: + type_name: type_name + inputs: + - file:///path/to/person.csv + - file:///path/to/person.csv + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + properties: + type_name: + type: string + inputs: + items: + example: file:///path/to/person.csv + type: string + type: array + column_mappings: + items: + $ref: '#/components/schemas/ColumnMapping' + type: array + type: object + x-body-name: vertex_mapping + EdgeMapping: + example: + inputs: + - inputs + - inputs + source_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + destination_vertex_mappings: + - column: + name: name + index: 0 + property: id + - column: + name: name + index: 0 + property: id + column_mappings: + - column: + name: name + index: 0 + property: property + - column: + name: name + index: 0 + property: property + type_triplet: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + properties: + type_triplet: + $ref: '#/components/schemas/EdgeMapping_type_triplet' + inputs: + items: + type: string + type: array + source_vertex_mappings: + items: + $ref: '#/components/schemas/EdgeMapping_source_vertex_mappings_inner' + type: array + destination_vertex_mappings: + items: + $ref: '#/components/schemas/EdgeMapping_destination_vertex_mappings_inner' + type: array + column_mappings: + items: + $ref: '#/components/schemas/ColumnMapping' + type: array + type: object + x-body-name: edge_mapping + ColumnMapping: + example: + column: + name: name + index: 0 + property: property + properties: + column: + $ref: '#/components/schemas/EdgeMapping_source_vertex_mappings_inner_column' + property: + description: must align with the schema + type: string + type: object + x-body-name: column_mapping + StartServiceRequest: + example: + graph_id: graph_id + properties: + graph_id: + type: string + x-body-name: start_service_request + ServiceStatus: + example: + graph_id: graph_id + bolt_port: 0 + hqps_port: 6 + gremlin_port: 1 + status: status + properties: + status: + type: string + graph_id: + type: string + bolt_port: + format: int32 + type: integer + hqps_port: + format: int32 + type: integer + gremlin_port: + format: int32 + type: integer + type: object + x-body-name: service_status + JobResponse: + example: + job_id: job_id + properties: + job_id: + type: string + type: object + x-body-name: job_response + JobStatus: + example: + start_time: 0 + log: log + end_time: 6 + id: id + detail: + key: "" + type: type + status: RUNNING + properties: + id: + type: string + type: + type: string + status: + enum: + - RUNNING + - SUCCESS + - FAILED + - CANCELLED + - WAITING + type: string + start_time: + format: int32 + type: integer + end_time: + format: int32 + type: integer + log: + description: URL or log string + type: string + detail: + additionalProperties: true + type: object + type: object + x-body-name: job_status + FixedChar_char: + properties: + fixed_char: + type: integer + required: + - fixed_char + type: object + VarChar_var_char: + properties: + max_length: + type: integer + required: + - max_length + type: object + StringType_string: + oneOf: + - $ref: '#/components/schemas/LongText' + - $ref: '#/components/schemas/FixedChar' + - $ref: '#/components/schemas/VarChar' + BaseVertexType_x_csr_params: + description: Used for storage optimization + properties: + max_vertex_num: + type: integer + type: object + BaseEdgeType_vertex_type_pair_relations_inner_x_csr_params: + description: Used for storage optimization + properties: + edge_storage_strategy: + enum: + - ONLY_IN + - ONLY_OUT + - BOTH_OUT_IN + type: string + sort_on_compaction: + type: boolean + type: object + BaseEdgeType_vertex_type_pair_relations_inner: + properties: + source_vertex: + type: string + destination_vertex: + type: string + relation: + enum: + - MANY_TO_MANY + - ONE_TO_MANY + - MANY_TO_ONE + - ONE_TO_ONE + type: string + x_csr_params: + $ref: '#/components/schemas/BaseEdgeType_vertex_type_pair_relations_inner_x_csr_params' + type: object + SchemaMapping_loading_config_format: + example: + metadata: + key: "" + type: type + properties: + type: + type: string + metadata: + additionalProperties: true + type: object + type: object + SchemaMapping_loading_config: + example: + format: + metadata: + key: "" + type: type + import_option: init + properties: + import_option: + enum: + - init + - overwrite + type: string + format: + $ref: '#/components/schemas/SchemaMapping_loading_config_format' + type: object + EdgeMapping_type_triplet: + description: "source label -> [edge label] -> destination label" + example: + edge: edge + source_vertex: source_vertex + destination_vertex: destination_vertex + properties: + edge: + type: string + source_vertex: + type: string + destination_vertex: + type: string + type: object + EdgeMapping_source_vertex_mappings_inner_column: + example: + name: name + index: 0 + properties: + index: + format: int32 + type: integer + name: + type: string + type: object + EdgeMapping_source_vertex_mappings_inner: + description: Mapping column to the primary key of source vertex + example: + column: + name: name + index: 0 + property: id + properties: + column: + $ref: '#/components/schemas/EdgeMapping_source_vertex_mappings_inner_column' + property: + example: id + type: string + type: object + EdgeMapping_destination_vertex_mappings_inner: + description: Mapping column to the primary key of destination vertex + example: + column: + name: name + index: 0 + property: id + properties: + column: + $ref: '#/components/schemas/EdgeMapping_source_vertex_mappings_inner_column' + property: + example: id + type: string + type: object + diff --git a/flex/interactive/sdk/java/pom.xml b/flex/interactive/sdk/java/pom.xml new file mode 100644 index 000000000000..a4608433dacf --- /dev/null +++ b/flex/interactive/sdk/java/pom.xml @@ -0,0 +1,364 @@ + + 4.0.0 + com.alibaba.graphscope + interactive-java-sdk + jar + interactive-java-sdk + 0.0.3 + https://github.com/alibaba/GraphScope/tree/main/flex/interactive + GraphScope Interactive Java SDK + + scm:git:git@github.com:openapitools/openapi-generator.git + scm:git:git@github.com:openapitools/openapi-generator.git + https://github.com/openapitools/openapi-generator + + + + + Apache-2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + + + GraphScope Team + graphscope@alibaba-inc.com + Alibaba GraphScope + https://graphscope.io + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + true + 128m + 512m + + -Xlint:all + -J-Xss4m + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + 1 + + **/DriverTest.java + + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + + + + + maven-dependency-plugin + 3.6.1 + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + test-jar + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + add_sources + generate-sources + + add-source + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + + jar + + + + + none + + + http.response.details + a + Http Response Details: + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar-no-fork + + + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + + + + .gitignore + + + + + + true + 4 + + + + + + + + + + 1.8 + + true + + + + + + + + + + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.1 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + com.squareup.okhttp3 + okhttp + ${okhttp-version} + + + com.squareup.okhttp3 + logging-interceptor + ${okhttp-version} + + + com.google.code.gson + gson + ${gson-version} + + + io.gsonfire + gson-fire + ${gson-fire-version} + + + org.apache.commons + commons-lang3 + ${commons-lang3-version} + + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-version} + provided + + + org.openapitools + jackson-databind-nullable + ${jackson-databind-nullable-version} + + + javax.ws.rs + jsr311-api + ${jsr311-api-version} + + + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs-api-version} + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + test + + + org.junit.platform + junit-platform-runner + ${junit-platform-runner.version} + test + + + org.apache.tinkerpop + tinkergraph-gremlin + ${tinkerpop.version} + + + org.apache.tinkerpop + gremlin-driver + ${tinkerpop.version} + + + org.neo4j.driver + neo4j-java-driver + ${neo4j.version} + + + + 1.8 + ${java.version} + ${java.version} + 1.9.0 + 4.11.0 + 2.10.1 + 3.14.0 + 0.2.6 + 1.3.5 + 5.10.0 + 1.10.0 + 2.1.1 + 1.1.1 + UTF-8 + 2.43.0 + 3.5.1 + 4.4.0 + + diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Driver.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Driver.java new file mode 100644 index 000000000000..eb2b8d71389d --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Driver.java @@ -0,0 +1,173 @@ +/* + * 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.client.impl.DefaultSession; +import com.alibaba.graphscope.interactive.openapi.model.*; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.neo4j.driver.SessionConfig; + +import java.util.logging.Logger; + +/** + * The entrypoint for all graphscope operations. + */ +public class Driver { + private static final Logger logger = Logger.getLogger(Driver.class.getName()); + + private final String host; + private final int port; + private Session defaultSession; + + public static Driver connect(String host, int port) { + return new Driver(host + ":" + port); + } + + public static Driver connect(String uri) { + return new Driver(uri); + } + + private Driver(String uri) { + // Parse uri + String[] parts = uri.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid uri: " + uri); + } + String host = parts[0]; + this.port = Integer.parseInt(parts[1]); + if (host.startsWith("http://")) { + this.host = host.substring(7); + } else { + this.host = host; + } + } + + private Driver(String host, int port) { + this.host = host; + this.port = port; + } + + /** + * Create a GraphScope session + * + * @return + */ + public Session session() { + return DefaultSession.newInstance(this.host, this.port); + } + + public Session getDefaultSession() { + if (defaultSession == null) { + defaultSession = session(); + } + return defaultSession; + } + + /** + * Create a neo4j session with the given session config. + * + * @param sessionConfig + * @return + */ + public org.neo4j.driver.Session getNeo4jSession(SessionConfig sessionConfig) { + return getNeo4jSessionImpl(sessionConfig); + } + + /** + * Create a neo4j session with default session config. + * + * @return a neo4j session + */ + public org.neo4j.driver.Session getNeo4jSession() { + SessionConfig sessionConfig = SessionConfig.builder().build(); + return getNeo4jSessionImpl(sessionConfig); + } + + public String getNeo4jEndpoint() { + Pair endpoint = getNeo4jEndpointImpl(); + if (endpoint == null) { + return null; + } + return "bolt://" + endpoint.getLeft() + ":" + endpoint.getRight(); + } + + /** + * Get the gremlin endpoint. + * + * @return a pair of host and port + */ + public Pair getGremlinEndpoint() { + return getGremlinEndpointImpl(); + } + + public Client getGremlinClient() { + Pair endpoint = getGremlinEndpointImpl(); + Cluster cluster = + Cluster.build() + .addContactPoint(endpoint.getLeft()) + .port(endpoint.getRight()) + .create(); + return cluster.connect(); + } + + /** + * Get a neo4j session, user can use this session to execute cypher query. + * For a more customized session, user can first get the bolt endpoint, and then create a session with the endpoint. + * + * @return a neo4j session + */ + private org.neo4j.driver.Session getNeo4jSessionImpl(SessionConfig sessionConfig) { + Pair endpoint = getNeo4jEndpointImpl(); + String boltUri = "neo4j://" + endpoint.getLeft() + ":" + endpoint.getRight(); + logger.info("Connecting to neo4j with uri: " + boltUri); + org.neo4j.driver.Driver driver = org.neo4j.driver.GraphDatabase.driver(boltUri); + return driver.session(sessionConfig); + } + + // TODO(zhangle): return null if bolt is not enabled + private Pair getNeo4jEndpointImpl() { + Session gsSession = getDefaultSession(); + Result serviceStatus = gsSession.getServiceStatus(); + if (!serviceStatus.isOk()) { + throw new RuntimeException( + "Failed to get service status: " + serviceStatus.getStatusMessage()); + } else { + ServiceStatus status = serviceStatus.getValue(); + Integer boltPort = status.getBoltPort(); + // Currently, we assume the host is the same as the gs server + return Pair.of(host, boltPort); + } + } + + // TODO(zhanglei): return null if gremlin is not enabled + private Pair getGremlinEndpointImpl() { + Session gsSession = getDefaultSession(); + Result serviceStatus = gsSession.getServiceStatus(); + if (!serviceStatus.isOk()) { + throw new RuntimeException( + "Failed to get service status: " + serviceStatus.getStatusMessage()); + } else { + ServiceStatus status = serviceStatus.getValue(); + Integer gremlinPort = status.getGremlinPort(); + // Currently, we assume the host is the same as the gs server + return Pair.of(host, gremlinPort); + } + } +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/EdgeInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/EdgeInterface.java new file mode 100644 index 000000000000..8f422049c0aa --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/EdgeInterface.java @@ -0,0 +1,44 @@ +/* + * 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.EdgeData; +import com.alibaba.graphscope.interactive.openapi.model.EdgeRequest; + +/** + * Interface for Create/Read/Update/Delete edge. + */ +public interface EdgeInterface { + Result getEdge( + String graphName, + String edgeLabel, + String srcLabel, + Object srcPrimaryKeyValue, + String dstLabel, + Object dstPrimaryKeyValue); + + Result addEdge(String graphName, EdgeRequest edgeRequest); + + Result deleteEdge( + String graphName, + String srcLabel, + Object srcPrimaryKeyValue, + String dstLabel, + Object dstPrimaryKeyValue); + + Result updateEdge(String graphName, EdgeRequest edgeRequest); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/GraphInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/GraphInterface.java new file mode 100644 index 000000000000..3f02d422b726 --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/GraphInterface.java @@ -0,0 +1,36 @@ +/* + * 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 java.util.List; + +/** + * All APIs about Graph Creation/Deletion/Updating/Getting, and dataloading. + */ +public interface GraphInterface { + Result bulkLoading(String graphId, SchemaMapping mapping); + + Result createGraph(CreateGraphRequest graph); + + Result deleteGraph(String graphId); + + Result getGraphSchema(String graphId); + + Result> getAllGraphs(); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/JobInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/JobInterface.java new file mode 100644 index 000000000000..66f45163e0a8 --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/JobInterface.java @@ -0,0 +1,32 @@ +/* + * 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 java.util.List; + +/** + * Get/Cancel/List jobs. + */ +public interface JobInterface { + Result cancelJob(String jobId); + + Result getJobStatus(String jobId); + + Result> listJobs(); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/ProcedureInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/ProcedureInterface.java new file mode 100644 index 000000000000..418ac349427f --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/ProcedureInterface.java @@ -0,0 +1,41 @@ +/* + * 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 java.util.List; + +/** + * All APIs about procedure management. + * TODO(zhanglei): differ between ProcedureRequest and Procedure + */ +public interface ProcedureInterface { + Result createProcedure( + String graphId, CreateProcedureRequest procedure); + + Result deleteProcedure(String graphId, String procedureName); + + Result getProcedure(String graphId, String procedureName); + + Result> listProcedures(String graphId); + + Result updateProcedure( + String graphId, String procedureId, UpdateProcedureRequest procedure); + + Result callProcedure(String graphId, QueryRequest request); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/QueryServiceInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/QueryServiceInterface.java new file mode 100644 index 000000000000..c84814591653 --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/QueryServiceInterface.java @@ -0,0 +1,32 @@ +/* + * 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.*; + +/** + * Manage the query interface. + */ +public interface QueryServiceInterface { + Result getServiceStatus(); + + Result restartService(); + + Result startService(StartServiceRequest service); + + Result stopService(); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Session.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Session.java new file mode 100644 index 000000000000..b5400b885b0f --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/Session.java @@ -0,0 +1,25 @@ +/* + * 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; + +public interface Session + extends VertexInterface, + EdgeInterface, + GraphInterface, + JobInterface, + ProcedureInterface, + QueryServiceInterface, + AutoCloseable {} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/VertexInterface.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/VertexInterface.java new file mode 100644 index 000000000000..6402314b8703 --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/VertexInterface.java @@ -0,0 +1,32 @@ +/* + * 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.*; + +/** + * Create/Update/Read/Delete vertex + */ +public interface VertexInterface { + Result addVertex(String graphId, VertexRequest request); + + Result updateVertex(String graphId, VertexRequest request); + + Result getVertex(String graphId, String label, Object primaryKey); + + Result deleteVertex(String graphId, String label, Object primaryKey); +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Result.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Result.java new file mode 100644 index 000000000000..457a8df45b66 --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Result.java @@ -0,0 +1,64 @@ +/* + * 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.common; + +import com.alibaba.graphscope.interactive.openapi.ApiException; +import com.alibaba.graphscope.interactive.openapi.ApiResponse; + +/*** + * A class which wrap the result of the API + */ +public class Result { + private final Status status; + private final T value; + + public Result(Status status, T value) { + this.status = status; + this.value = value; + } + + public Status getStatus() { + return status; + } + + public String getStatusMessage() { + return status.getMessage(); + } + + public T getValue() { + return value; + } + + public static Result ok(T value) { + return new Result(Status.Ok(), value); + } + + public static Result error(String message) { + return new Result(new Status(Status.StatusCode.kUnknown, message), null); + } + + public static Result fromException(ApiException exception) { + return new Result(Status.fromException(exception), null); + } + + public static Result fromResponse(ApiResponse response) { + return new Result(Status.fromResponse(response), response.getData()); + } + + public boolean isOk() { + return status.IsOk(); + } +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Status.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Status.java new file mode 100644 index 000000000000..85bd2e8e75ae --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/common/Status.java @@ -0,0 +1,101 @@ +/* + * 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.common; + +import com.alibaba.graphscope.interactive.openapi.ApiException; +import com.alibaba.graphscope.interactive.openapi.ApiResponse; + +/** + * Mapping http status code to our status code, along with a message + */ +public class Status { + enum StatusCode { + kOk, // 200 + kBadRequest, // 400 + kForbidden, // 403 + kNotFound, // 404 + kServerInternalError, // 500 + kServiceUnavailable, // 503 + kUnknown, // default + } + + private final StatusCode code; + private final String message; + + public Status() { + this.code = StatusCode.kUnknown; + this.message = ""; + } + + public boolean IsOk() { + return this.code == StatusCode.kOk; + } + + public String getMessage() { + return message; + } + + public Status(StatusCode code, String message) { + this.code = code; + this.message = message; + } + + public static Status ServerInternalError(String message) { + return new Status(StatusCode.kServerInternalError, message); + } + + public static Status fromException(ApiException exception) { + // mapping exception's http code to our status code + switch (exception.getCode()) { + case 400: + return new Status(StatusCode.kBadRequest, exception.getMessage()); + case 403: + return new Status(StatusCode.kForbidden, exception.getMessage()); + case 404: + return new Status(StatusCode.kNotFound, exception.getMessage()); + case 500: + return new Status(StatusCode.kServerInternalError, exception.getMessage()); + case 503: + return new Status(StatusCode.kServiceUnavailable, exception.getMessage()); + default: + return new Status(StatusCode.kUnknown, exception.getMessage()); + } + } + + public static Status fromResponse(ApiResponse response) { + // mapping response's http code to our status code + switch (response.getStatusCode()) { + case 200: + return new Status(StatusCode.kOk, ""); + case 400: + return new Status(StatusCode.kBadRequest, ""); + case 403: + return new Status(StatusCode.kForbidden, ""); + case 404: + return new Status(StatusCode.kNotFound, ""); + case 500: + return new Status(StatusCode.kServerInternalError, ""); + case 503: + return new Status(StatusCode.kServiceUnavailable, ""); + default: + return new Status(StatusCode.kUnknown, ""); + } + } + + public static Status Ok() { + return new Status(StatusCode.kOk, ""); + } +} diff --git a/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/impl/DefaultSession.java b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/impl/DefaultSession.java new file mode 100644 index 000000000000..6919b67ce8da --- /dev/null +++ b/flex/interactive/sdk/java/src/main/java/com/alibaba/graphscope/interactive/client/impl/DefaultSession.java @@ -0,0 +1,439 @@ +/* + * 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.impl; + +import com.alibaba.graphscope.interactive.client.Session; +import com.alibaba.graphscope.interactive.client.common.Result; +import com.alibaba.graphscope.interactive.openapi.ApiClient; +import com.alibaba.graphscope.interactive.openapi.ApiException; +import com.alibaba.graphscope.interactive.openapi.ApiResponse; +import com.alibaba.graphscope.interactive.openapi.api.*; +import com.alibaba.graphscope.interactive.openapi.model.*; + +import java.io.Closeable; +import java.util.List; + +/*** + * A default implementation of the GraphScope interactive session interface. + * Based on the code generated by OpenAPI Generator. + */ +public class DefaultSession implements Session { + private final AdminServiceGraphManagementApi graphApi; + private final AdminServiceJobManagementApi jobApi; + private final AdminServiceProcedureManagementApi procedureApi; + private final AdminServiceServiceManagementApi serviceApi; + private final GraphServiceVertexManagementApi vertexApi; + private final GraphServiceEdgeManagementApi edgeApi; + private final QueryServiceApi queryApi; + + private static final int DEFAULT_READ_TIMEOUT = 30000; + private static final int DEFAULT_WRITE_TIMEOUT = 30000; + + private final ApiClient client; + + public static DefaultSession newInstance(String uri) { + return new DefaultSession(uri); + } + + public static DefaultSession newInstance(String host, int port) { + return new DefaultSession("http://" + host + ":" + port); + } + + /** + * Create a default GraphScope Interactive Session. + * + * @param uri should be in the format "http://host:port" + */ + private DefaultSession(String uri) { + client = new ApiClient(); + client.setBasePath(uri); + client.setReadTimeout(DEFAULT_READ_TIMEOUT); + client.setWriteTimeout(DEFAULT_WRITE_TIMEOUT); + + graphApi = new AdminServiceGraphManagementApi(client); + jobApi = new AdminServiceJobManagementApi(client); + procedureApi = new AdminServiceProcedureManagementApi(client); + serviceApi = new AdminServiceServiceManagementApi(client); + vertexApi = new GraphServiceVertexManagementApi(client); + edgeApi = new GraphServiceEdgeManagementApi(client); + queryApi = new QueryServiceApi(client); + } + + @Override + public Result getEdge( + String graphName, + String edgeLabel, + String srcLabel, + Object srcPrimaryKeyValue, + String dstLabel, + Object dstPrimaryKeyValue) { + try { + ApiResponse response = + edgeApi.getEdgeWithHttpInfo( + graphName, + edgeLabel, + srcLabel, + srcPrimaryKeyValue, + dstLabel, + dstPrimaryKeyValue); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result addEdge(String graphName, EdgeRequest edgeRequest) { + try { + ApiResponse response = edgeApi.addEdgeWithHttpInfo(graphName, edgeRequest); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result deleteEdge( + String graphName, + String srcLabel, + Object srcPrimaryKeyValue, + String dstLabel, + Object dstPrimaryKeyValue) { + try { + ApiResponse response = + edgeApi.deleteEdgeWithHttpInfo( + graphName, srcLabel, srcPrimaryKeyValue, dstLabel, dstPrimaryKeyValue); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result updateEdge(String graphName, EdgeRequest edgeRequest) { + try { + ApiResponse response = edgeApi.updateEdgeWithHttpInfo(graphName, edgeRequest); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result bulkLoading(String graphId, SchemaMapping mapping) { + try { + ApiResponse response = + graphApi.createDataloadingJobWithHttpInfo(graphId, mapping); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result createGraph(CreateGraphRequest graph) { + try { + ApiResponse response = graphApi.createGraphWithHttpInfo(graph); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result deleteGraph(String graphId) { + try { + ApiResponse response = graphApi.deleteGraphWithHttpInfo(graphId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result getGraphSchema(String graphId) { + try { + ApiResponse response = graphApi.getSchemaWithHttpInfo(graphId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result> getAllGraphs() { + try { + ApiResponse> response = graphApi.listGraphsWithHttpInfo(); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result cancelJob(String jobId) { + try { + ApiResponse response = jobApi.deleteJobByIdWithHttpInfo(jobId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result getJobStatus(String jobId) { + try { + ApiResponse response = jobApi.getJobByIdWithHttpInfo(jobId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result> listJobs() { + try { + ApiResponse> response = jobApi.listJobsWithHttpInfo(); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result createProcedure( + String graphId, CreateProcedureRequest procedure) { + try { + ApiResponse response = + procedureApi.createProcedureWithHttpInfo(graphId, procedure); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result deleteProcedure(String graphId, String procedureName) { + try { + ApiResponse response = + procedureApi.deleteProcedureWithHttpInfo(graphId, procedureName); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result getProcedure(String graphId, String procedureId) { + try { + ApiResponse response = + procedureApi.getProcedureWithHttpInfo(graphId, procedureId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result> listProcedures(String graphId) { + try { + ApiResponse> response = + procedureApi.listProceduresWithHttpInfo(graphId); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result updateProcedure( + String graphId, String procedureId, UpdateProcedureRequest procedure) { + try { + ApiResponse response = + procedureApi.updateProcedureWithHttpInfo(graphId, procedureId, procedure); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result callProcedure(String graphName, QueryRequest request) { + try { + ApiResponse response = + queryApi.procCallWithHttpInfo(graphName, request); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result getServiceStatus() { + try { + ApiResponse response = serviceApi.getServiceStatusWithHttpInfo(); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result restartService() { + try { + ApiResponse response = serviceApi.restartServiceWithHttpInfo(); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result startService(StartServiceRequest service) { + try { + ApiResponse response = serviceApi.startServiceWithHttpInfo(service); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result stopService() { + try { + ApiResponse response = serviceApi.stopServiceWithHttpInfo(); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result addVertex(String graphId, VertexRequest request) { + try { + ApiResponse response = vertexApi.addVertexWithHttpInfo(graphId, request); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result updateVertex(String graphId, VertexRequest request) { + try { + ApiResponse response = vertexApi.updateVertexWithHttpInfo(graphId, request); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result getVertex(String graphId, String label, Object primaryKey) { + try { + ApiResponse response = + vertexApi.getVertexWithHttpInfo(graphId, label, primaryKey); + return Result.fromResponse(response); + } catch (ApiException e) { + e.printStackTrace(); + return Result.fromException(e); + } + } + + @Override + public Result deleteVertex(String graphId, String label, Object primaryKey) { + try { + ApiResponse 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 + + + +