diff --git a/.gitignore b/.gitignore index 64d448025de3..39e230a42e00 100644 --- a/.gitignore +++ b/.gitignore @@ -157,6 +157,12 @@ flex/interactive/sdk/python/gs_interactive/rest.py !flex/interactive/sdk/python/gs_interactive/client/generated/__init__.py !flex/interactive/sdk/python/gs_interactive/models/long_text.py +interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/models/ +interactive_engine/groot-http/.openapi-generator/ +interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/*.java +interactive_engine/groot-http/src/main/java/org/ +interactive_engine/groot-http/src/main/resources/ +interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/V1Api.java **/.cache/ diff --git a/charts/graphscope-store/templates/configmap.yaml b/charts/graphscope-store/templates/configmap.yaml index c690d3f33809..54cb1b1359ae 100644 --- a/charts/graphscope-store/templates/configmap.yaml +++ b/charts/graphscope-store/templates/configmap.yaml @@ -171,6 +171,10 @@ data: # export MALLOC_CONF=prof:true,lg_prof_interval:29,lg_prof_sample:19,prof_prefix=/tmp export RUST_BACKTRACE=1 + if [ "$ROLE" = "frontend" ]; then + echo "Starting groot-http Spring Boot service..." + java -jar ${GRAPHSCOPE_HOME}/groot/lib/groot-http-0.0.1-SNAPSHOT.jar > /var/log/graphscope/groot-http.log 2>&1 & + fi ${GRAPHSCOPE_HOME}/groot/bin/store_ctl.sh start ${ROLE} # || sleep infinity portal_setup.sh: |- #!/bin/bash diff --git a/flex/interactive/sdk/generate_sdk.sh b/flex/interactive/sdk/generate_sdk.sh index e38c33bc9920..fd45191d5cd0 100755 --- a/flex/interactive/sdk/generate_sdk.sh +++ b/flex/interactive/sdk/generate_sdk.sh @@ -81,6 +81,20 @@ function do_gen_python() { eval $cmd } +function do_gen_spring() { + echo "Generating Spring API" + OUTPUT_PATH="${CUR_DIR}/../../../interactive_engine/groot-http" + GROOT_PACKAGE_NAME="com.alibaba.graphscope.groot" + GROOT_ARTIFACT_ID="groot-http" + additional_properties="apiPackage=${GROOT_PACKAGE_NAME}.service.api,modelPackage=${GROOT_PACKAGE_NAME}.service.models,artifactId=${GROOT_ARTIFACT_ID},groupId=${GROUP_ID},invokerPackage=${GROOT_PACKAGE_NAME}" + export JAVA_OPTS="-Dlog.level=${LOG_LEVEL}" + cmd="openapi-generator-cli generate -i ${OPENAPI_SPEC_PATH} -g spring -o ${OUTPUT_PATH}" + cmd=" ${cmd} --additional-properties=${additional_properties}" + echo "Running command: ${cmd}" + + eval $cmd +} + function do_gen() { # expect only one argument if [ $# -ne 1 ]; then @@ -97,6 +111,9 @@ function do_gen() { python) do_gen_python ;; + spring) + do_gen_spring + ;; *) err "Unsupported language: $lang" usage diff --git a/flex/openapi/openapi_interactive.yaml b/flex/openapi/openapi_interactive.yaml index bdbf4474907b..92b307d9a265 100644 --- a/flex/openapi/openapi_interactive.yaml +++ b/flex/openapi/openapi_interactive.yaml @@ -166,14 +166,228 @@ paths: required: true schema: type: string - description: The id of graph to delete + description: The id of graph to get schema responses: - '200': + 200: description: successful operation content: application/json: schema: $ref: '#/components/schemas/GetGraphSchemaResponse' + 500: + $ref: "#/components/responses/500" + /v1/graph/{graph_id}/schema/vertex: + post: + tags: + - AdminService/GraphManagement + description: Create a vertex type + operationId: createVertexType + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateVertexType' + responses: + '200': + description: Successfully created the vertex type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + + delete: + tags: + - AdminService/GraphManagement + description: Delete a vertex type by name + operationId: deleteVertexTypeByName + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + - name: type_name + in: query + required: true + schema: + type: string + responses: + '200': + description: Successfully deleted the vertex type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + + put: + tags: + - AdminService/GraphManagement + description: Update a vertex type to add more properties + operationId: updateVertexType + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateVertexType' + responses: + '200': + description: Successfully updated the vertex type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + + /v1/graph/{graph_id}/schema/edge: + post: + tags: + - AdminService/GraphManagement + description: Create a edge type + operationId: createEdgeType + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEdgeType' + responses: + '200': + description: Successful created the edge type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + + delete: + tags: + - AdminService/GraphManagement + description: Delete an edge type by name + operationId: deleteEdgeTypeByName + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + - name: type_name + in: query + required: true + schema: + type: string + - name: source_vertex_type + in: query + required: true + schema: + type: string + - name: destination_vertex_type + in: query + required: true + schema: + type: string + responses: + '200': + description: Successfully deleted the edge type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + + put: + tags: + - AdminService/GraphManagement + description: Update an edge type to add more properties + operationId: updateEdgeType + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEdgeType' + responses: + 200: + description: Successfully updated the edge type + content: + application/json: + schema: + $ref: '#/components/schemas/APIResponse' + 400: + $ref: "#/components/responses/400" + 500: + $ref: "#/components/responses/500" + + /v1/graph/{graph_id}/snapshot/{snapshot_id}/status: + get: + tags: + - AdminService/GraphManagement + description: Get the status of a snapshot by id + operationId: get_snapshot_status + parameters: + - name: graph_id + in: path + required: true + schema: + type: string + - name: snapshot_id + in: path + required: true + schema: + type: integer + responses: + '200': + description: the status of a snapshot_id of whether it is available + content: + application/json: + schema: + type: boolean + '400': + $ref: "#/components/responses/400" + '500': + $ref: "#/components/responses/500" + /v1/graph/{graph_id}/statistics: get: tags: @@ -626,7 +840,7 @@ paths: post: tags: - GraphService/VertexManagement - summary: Add vertex to the graph + summary: Add vertex (and edge) to the graph operationId: add_vertex parameters: - name: graph_id @@ -635,7 +849,7 @@ paths: schema: type: string description: | - Add the provided vertex to the specified graph. + Add the provided vertex (and edge) to the specified graph. requestBody: required: true content: @@ -660,7 +874,6 @@ paths: properties: - name: "weight" value: 0.2 - responses: '200': description: Successfully created vertex @@ -712,7 +925,7 @@ paths: summary: Update vertex's property operationId: update_vertex description: | - Remove the vertex from the specified graph. + Update the vertex with the provided properties to the specified graph. parameters: - name: graph_id in : path @@ -723,7 +936,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/VertexRequest' + type: array + items: + $ref: '#/components/schemas/VertexRequest' example: label: "person" primary_key_value: 2 @@ -779,18 +994,15 @@ paths: 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' + requestBody: + description: The label and primary key values of the vertex to be deleted. + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DeleteVertexRequest' responses: '200': description: Successfully delete vertex @@ -1005,7 +1217,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/EdgeRequest' + type: array + items: + $ref: '#/components/schemas/EdgeRequest' example: src_label: "person" dst_label: "software" @@ -1066,35 +1280,15 @@ paths: 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 + requestBody: + description: The label and primary key values of the src and dst vertices, and the edge label. + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DeleteEdgeRequest' responses: '200': description: Successfully delete edge @@ -1131,6 +1325,7 @@ paths: example: code: 103 message: "Internal error" + /v1/graph/{graph_id}/query: post: tags: @@ -1304,6 +1499,7 @@ paths: example: code: 103 message: "Internal error" + components: schemas: AnyValue: {} @@ -1320,91 +1516,137 @@ components: $ref: '#/components/schemas/AnyValue' PrimitiveType: x-body-name: primitive_type + allOf: + - $ref: '#/components/schemas/BaseGSDataType' + - 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, DT_STRING] + # The DT_STRING is added for backward compatibility, it should be replaced by StringType + example: DT_SIGNED_INT32 + BaseStringType: + x-body-name: base_string_type type: object - required: - - primitive_type + required: + - string_type properties: - primitive_type: + string_type: type: string - enum: [DT_SIGNED_INT32, DT_UNSIGNED_INT32, DT_SIGNED_INT64, DT_UNSIGNED_INT64, - DT_BOOL, DT_FLOAT, DT_DOUBLE, DT_STRING] - # The DT_STRING is added for backward compatibility, it should be replaced by StringType - example: DT_SIGNED_INT32 + discriminator: + propertyName: string_type LongText: x-body-name: long_text - type: object - additionalProperties: false - required: - - long_text - properties: - long_text: - type: string - nullable: true + allOf: + - $ref: '#/components/schemas/BaseStringType' + - type: object + additionalProperties: false + required: + - long_text + properties: + long_text: + type: string + nullable: true FixedChar: x-body-name: fixed_char - type: object - additionalProperties: false - required: - - char - properties: - char: - type: object + allOf: + - $ref: '#/components/schemas/BaseStringType' + - type: object + additionalProperties: false required: - - fixed_length + - char properties: - fixed_length: - type: integer + char: + type: object + required: + - fixed_length + properties: + fixed_length: + type: integer VarChar: x-body-name: var_char - type: object - additionalProperties: false - required: - - var_char - properties: - var_char: - type: object + allOf: + - $ref: '#/components/schemas/BaseStringType' + - type: object + additionalProperties: false required: - - max_length + - var_char properties: - max_length: - type: integer + 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 + allOf: + - $ref: '#/components/schemas/BaseGSDataType' + - type: object + required: + - string + properties: + string: + oneOf: + - $ref: '#/components/schemas/LongText' + - $ref: '#/components/schemas/FixedChar' + - $ref: '#/components/schemas/VarChar' + BaseTemporalType: + x-body-name: base_temporal_type type: object required: - - timestamp + - temporal_type properties: - timestamp: + temporal_type: type: string + discriminator: + propertyName: temporal_type + TimeStampType: + x-body-name: time_stamp_type + allOf: + - $ref: '#/components/schemas/BaseTemporalType' + - type: object + required: + - timestamp + properties: + timestamp: + type: string DateType: x-body-name: date_type - type: object - required: - - date32 - properties: - date32: - type: string + allOf: + - $ref: '#/components/schemas/BaseTemporalType' + - type: object + required: + - date32 + properties: + date32: + type: string TemporalType: x-body-name: temporal_type + allOf: + - $ref: '#/components/schemas/BaseGSDataType' + - type: object + required: + - temporal + properties: + temporal: + oneOf: + - $ref: '#/components/schemas/TimeStampType' + - $ref: '#/components/schemas/DateType' + BaseGSDataType: + x-body-name: base_gs_data_type type: object required: - - temporal + - type_name properties: - temporal: - oneOf: - - $ref: '#/components/schemas/TimeStampType' - - $ref: '#/components/schemas/DateType' + type_name: + type: string + discriminator: + propertyName: type_name GSDataType: x-body-name: gs_data_type oneOf: @@ -1444,18 +1686,33 @@ components: type: object required: - label - - primary_key_value + - primary_key_values - properties properties: label: type: string example: person - primary_key_value: - $ref: '#/components/schemas/AnyValue' + primary_key_values: + type: array + items: + $ref: '#/components/schemas/Property' properties: type: array items: $ref: '#/components/schemas/Property' + DeleteVertexRequest: + x-body-name: delete_vertex_request + type: object + properties: + label: + type: string + description: The label name of the vertex. + example: person + primary_key_values: + type: array + description: Primary key values for the vertex. + items: + $ref: '#/components/schemas/Property' VertexData: x-body-name: vertex_data type: object @@ -1504,8 +1761,8 @@ components: - src_label - dst_label - edge_label - - src_primary_key_value - - dst_primary_key_value + - src_primary_key_values + - dst_primary_key_values properties: src_label: type: string @@ -1516,14 +1773,44 @@ components: edge_label: type: string example: created - src_primary_key_value: - $ref: '#/components/schemas/AnyValue' - dst_primary_key_value: - $ref: '#/components/schemas/AnyValue' + src_primary_key_values: + type: array + items: + $ref: '#/components/schemas/Property' + dst_primary_key_values: + type: array + items: + $ref: '#/components/schemas/Property' properties: type: array items: $ref: '#/components/schemas/Property' + DeleteEdgeRequest: + x-body-name: delete_edge_request + type: object + properties: + edge_label: + type: string + description: The label name of the edge. + example: created + src_label: + type: string + description: The label name of the source vertex. + example: person + dst_label: + type: string + description: The label name of the destination vertex. + example: software + src_primary_key_values: + type: array + description: Primary key values for the source vertex. + items: + $ref: '#/components/schemas/Property' + dst_primary_key_values: + type: array + description: Primary key values for the destination vertex. + items: + $ref: '#/components/schemas/Property' QueryRequest: x-body-name: query_request type: object diff --git a/interactive_engine/assembly/pom.xml b/interactive_engine/assembly/pom.xml index 39b0aa02ea32..2ddf36a64810 100644 --- a/interactive_engine/assembly/pom.xml +++ b/interactive_engine/assembly/pom.xml @@ -64,6 +64,11 @@ groot-server ${project.version} + + com.alibaba.graphscope + groot-http + ${project.version} + com.alibaba.graphscope executor diff --git a/interactive_engine/groot-http/.openapi-generator-ignore b/interactive_engine/groot-http/.openapi-generator-ignore new file mode 100644 index 000000000000..e5386b6dd5a9 --- /dev/null +++ b/interactive_engine/groot-http/.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 + +pom.xml +src/main/java/com/alibaba/graphscope/groot/service/impl +src/main/java/com/alibaba/graphscope/groot/service/api/ApiUtil.java +src/main/java/com/alibaba/graphscope/groot/service/api/V1ApiController.java \ No newline at end of file diff --git a/interactive_engine/groot-http/README.md b/interactive_engine/groot-http/README.md new file mode 100644 index 000000000000..5cd22b6081a2 --- /dev/null +++ b/interactive_engine/groot-http/README.md @@ -0,0 +1,21 @@ +# OpenAPI generated server + +Spring Boot Server + +## Overview +This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. +By using the [OpenAPI-Spec](https://openapis.org), you can easily generate a server stub. +This is an example of building a OpenAPI-enabled server in Java using the SpringBoot framework. + + +The underlying library integrating OpenAPI to Spring Boot is [springdoc](https://springdoc.org). +Springdoc will generate an OpenAPI v3 specification based on the generated Controller and Model classes. +The specification is available to download using the following url: +http://localhost:8080/v3/api-docs/ + +Start your server as a simple java application + +You can view the api documentation in swagger-ui by pointing to +http://localhost:8080/swagger-ui.html + +Change default port value in application.properties \ No newline at end of file diff --git a/interactive_engine/groot-http/pom.xml b/interactive_engine/groot-http/pom.xml new file mode 100644 index 000000000000..b2e34ae53386 --- /dev/null +++ b/interactive_engine/groot-http/pom.xml @@ -0,0 +1,119 @@ + + + interactive-parent + com.alibaba.graphscope + ${revision} + ../pom.xml + + 4.0.0 + groot-http + jar + ${project.groupId}:${project.artifactId} + + 1.8 + ${java.version} + ${java.version} + UTF-8 + 1.6.14 + 5.3.1 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.data + spring-data-commons + + + + org.springdoc + springdoc-openapi-ui + ${springdoc.version} + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + org.springframework.boot + spring-boot-starter-validation + + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter + + + com.alibaba.graphscope + groot-client + + + + src/main/java + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + generate-sources + generate-sources + + exec + + + /bin/bash + + ${project.basedir}/pre-generate.sh + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + com.alibaba.graphscope.groot.OpenApiGeneratorApplication + + + + + diff --git a/interactive_engine/groot-http/pre-generate.sh b/interactive_engine/groot-http/pre-generate.sh new file mode 100755 index 000000000000..25543c14e761 --- /dev/null +++ b/interactive_engine/groot-http/pre-generate.sh @@ -0,0 +1,19 @@ +#!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +SDK_SCRIPT_PATH="${SCRIPT_DIR}/../../flex/interactive/sdk/generate_sdk.sh" +LANGUAGE="spring" + +if [ ! -f "$SDK_SCRIPT_PATH" ]; then + echo "Error: SDK generation script not found at $SDK_SCRIPT_PATH" + exit 1 +fi + +echo "Generating Spring files using OpenAPI Generator..." +bash "$SDK_SCRIPT_PATH" -g "$LANGUAGE" + +if [ $? -ne 0 ]; then + echo "Failed to generate Spring files" + exit 1 +else + echo "Successfully generated Spring files" +fi \ No newline at end of file diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/ApiUtil.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/ApiUtil.java new file mode 100644 index 000000000000..2e1dd6b8cf58 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/ApiUtil.java @@ -0,0 +1,39 @@ +package com.alibaba.graphscope.groot.service.api; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.context.request.NativeWebRequest; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +public class ApiUtil { + public static void setExampleResponse( + NativeWebRequest req, String contentType, String example) { + try { + HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class); + res.setCharacterEncoding("UTF-8"); + res.addHeader("Content-Type", contentType); + res.getWriter().print(example); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static ResponseEntity createErrorResponse(HttpStatus status, String message) { + return ResponseEntity.status(status).body(String.format("{\"error\": \"%s\"}", message)); + } + + public static ResponseEntity createSuccessResponse(String message, long snapshotId) { + return ResponseEntity.status(HttpStatus.OK) + .body( + String.format( + "{\"message\": \"%s\", \"snapshot id\": %d}", message, snapshotId)); + } + + public static ResponseEntity createSuccessResponse(String message) { + return ResponseEntity.status(HttpStatus.OK) + .body(String.format("{\"message\": \"%s\"}", message)); + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/V1ApiController.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/V1ApiController.java new file mode 100644 index 000000000000..3db37441e571 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/api/V1ApiController.java @@ -0,0 +1,335 @@ +package com.alibaba.graphscope.groot.service.api; + +import com.alibaba.graphscope.groot.service.impl.EdgeManagementService; +import com.alibaba.graphscope.groot.service.impl.SchemaManagementService; +import com.alibaba.graphscope.groot.service.impl.VertexManagementService; +import com.alibaba.graphscope.groot.service.models.CreateEdgeType; +import com.alibaba.graphscope.groot.service.models.CreateGraphRequest; +import com.alibaba.graphscope.groot.service.models.CreateGraphResponse; +import com.alibaba.graphscope.groot.service.models.CreateVertexType; +import com.alibaba.graphscope.groot.service.models.DeleteEdgeRequest; +import com.alibaba.graphscope.groot.service.models.DeleteVertexRequest; +import com.alibaba.graphscope.groot.service.models.EdgeRequest; +import com.alibaba.graphscope.groot.service.models.GetGraphSchemaResponse; +import com.alibaba.graphscope.groot.service.models.VertexEdgeRequest; +import com.alibaba.graphscope.groot.service.models.VertexRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import javax.validation.Valid; + +@RestController +@RequestMapping("${openapi.graphScopeInteractiveAPIV03.base-path:/v1/graph}") +public class V1ApiController implements V1Api { + + private final VertexManagementService vertexManagementService; + private final EdgeManagementService edgeManagementService; + private final SchemaManagementService schemaManagementService; + + @Autowired + public V1ApiController( + VertexManagementService vertexService, + EdgeManagementService edgeService, + SchemaManagementService schemaManagementService) { + this.vertexManagementService = vertexService; + this.edgeManagementService = edgeService; + this.schemaManagementService = schemaManagementService; + } + + @Override + @PostMapping( + value = "/{graph_id}/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addVertex( + @PathVariable("graph_id") String graphId, + @RequestBody @Valid VertexEdgeRequest vertexEdgeRequest) { + try { + long si; + if (vertexEdgeRequest.getEdgeRequest() == null) { + si = vertexManagementService.addVertices(vertexEdgeRequest.getVertexRequest()); + } else { + si = vertexManagementService.addVerticesAndEdges(vertexEdgeRequest); + } + return ApiUtil.createSuccessResponse("Vertices and edges added successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to add vertices and edges: " + e.getMessage()); + } + } + + @Override + @DeleteMapping( + value = "/{graph_id}/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity deleteVertex( + @PathVariable("graph_id") String graphId, + @RequestBody(required = true) List deleteVertexRequest) { + try { + long si = vertexManagementService.deleteVertices(deleteVertexRequest); + return ApiUtil.createSuccessResponse("Vertices deleted successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to delete vertices: " + e.getMessage()); + } + } + + @Override + @PutMapping( + value = "/{graph_id}/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateVertex( + @PathVariable("graph_id") String graphId, + @RequestBody(required = false) List vertexRequest) { + try { + long si = vertexManagementService.updateVertices(vertexRequest); + return ApiUtil.createSuccessResponse("Vertices updated successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to update vertices: " + e.getMessage()); + } + } + + @Override + @PostMapping( + value = "/{graph_id}/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addEdge( + @PathVariable("graph_id") String graphId, + @RequestBody(required = true) List edgeRequest) { + try { + long si = edgeManagementService.addEdges(edgeRequest); + return ApiUtil.createSuccessResponse("Edges added successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, "Failed to add edges: " + e.getMessage()); + } + } + + @Override + @DeleteMapping( + value = "/{graph_id}/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity deleteEdge( + @PathVariable("graph_id") String graphId, + @RequestBody(required = true) List deleteEdgeRequest) { + try { + long si = edgeManagementService.deleteEdges(deleteEdgeRequest); + return ApiUtil.createSuccessResponse("Edges deleted successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, "Failed to delete edges: " + e.getMessage()); + } + } + + @Override + @PutMapping( + value = "/{graph_id}/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateEdge( + @PathVariable("graph_id") String graphId, + @RequestBody(required = false) List edgeRequest) { + try { + long si = edgeManagementService.updateEdges(edgeRequest); + return ApiUtil.createSuccessResponse("Edges updated successfully", si); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, "Failed to update edges: " + e.getMessage()); + } + } + + @Override + @PostMapping( + value = "/{graph_id}/schema/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createVertexType( + @PathVariable("graph_id") String graphId, + @RequestBody @Validated CreateVertexType createVertexType) { + try { + schemaManagementService.createVertexType(createVertexType); + return ApiUtil.createSuccessResponse("Vertex type created successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to create vertex type: " + e.getMessage()); + } + } + + @Override + @DeleteMapping( + value = "/{graph_id}/schema/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity deleteVertexTypeByName( + @PathVariable("graph_id") String graphId, + @RequestParam(value = "type_name", required = true) String typeName) { + try { + schemaManagementService.deleteVertexType(typeName); + return ApiUtil.createSuccessResponse("Vertex type deleted successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to delete vertex type" + e.getMessage()); + } + } + + @Override + @PutMapping( + value = "/{graph_id}/schema/vertex", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateVertexType( + @PathVariable("graph_id") String graphId, + @RequestBody @Validated CreateVertexType updateVertexType) { + try { + schemaManagementService.updateVertexType(updateVertexType); + return ApiUtil.createSuccessResponse("Vertex type updated successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to update vertex type: " + e.getMessage()); + } + } + + @Override + @PostMapping( + value = "/{graph_id}/schema/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createEdgeType( + @PathVariable("graph_id") String graphId, + @RequestBody @Validated CreateEdgeType createEdgeType) { + try { + schemaManagementService.createEdgeType(createEdgeType); + return ApiUtil.createSuccessResponse("Edge type created successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to create edge type: " + e.getMessage()); + } + } + + @Override + @DeleteMapping( + value = "/{graph_id}/schema/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity deleteEdgeTypeByName( + @PathVariable("graph_id") String graphId, + @RequestParam(value = "type_name", required = true) String typeName, + @RequestParam(value = "source_vertex_type", required = true) String sourceVertexType, + @RequestParam(value = "destination_vertex_type", required = true) + String destinationVertexType) { + try { + schemaManagementService.deleteEdgeType( + typeName, sourceVertexType, destinationVertexType); + return ApiUtil.createSuccessResponse("Edge type deleted successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to delete edge type: " + e.getMessage()); + } + } + + @Override + @PutMapping( + value = "/{graph_id}/schema/edge", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateEdgeType( + @PathVariable("graph_id") String graphId, + @RequestBody @Validated CreateEdgeType updateEdgeType) { + try { + schemaManagementService.updateEdgeType(updateEdgeType); + return ApiUtil.createSuccessResponse("Edge type updated successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to update edge type: " + e.getMessage()); + } + } + + @Override + @PostMapping( + value = "", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createGraph( + @RequestBody @Validated CreateGraphRequest createGraphRequest) { + try { + schemaManagementService.importSchema(createGraphRequest.getSchema()); + CreateGraphResponse response = new CreateGraphResponse(); + // a default graph id, since groot does not support multiple graphs + response.setGraphId("0"); + return ResponseEntity.status(HttpStatus.OK).body(response); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + } + + @Override + @GetMapping(value = "/{graph_id}/schema", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getSchema( + @PathVariable("graph_id") String graphId) { + try { + GetGraphSchemaResponse response = schemaManagementService.getSchema(); + return ResponseEntity.status(HttpStatus.OK).body(response); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + } + + @Override + @DeleteMapping(value = "/{graph_id}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity deleteGraph(@PathVariable("graph_id") String graphId) { + try { + schemaManagementService.dropSchema(); + return ApiUtil.createSuccessResponse("Graph schema deleted successfully"); + } catch (Exception e) { + return ApiUtil.createErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "Failed to delete graph schema: " + e.getMessage()); + } + } + + @Override + @PostMapping( + value = "/{graph_id}/snapshot/{snapshot_id}/status", + produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getSnapshotStatus( + @PathVariable("graph_id") String graphId, + @PathVariable("snapshot_id") Integer snapshotId) { + try { + boolean res = schemaManagementService.remoteFlush(snapshotId); + return ResponseEntity.status(HttpStatus.OK).body(res); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/DtoConverter.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/DtoConverter.java new file mode 100644 index 000000000000..a60cc927fefc --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/DtoConverter.java @@ -0,0 +1,201 @@ +package com.alibaba.graphscope.groot.service.impl; + +import com.alibaba.graphscope.groot.sdk.schema.Edge; +import com.alibaba.graphscope.groot.sdk.schema.EdgeLabel; +import com.alibaba.graphscope.groot.sdk.schema.EdgeLabel.EdgeRelation; +import com.alibaba.graphscope.groot.sdk.schema.Vertex; +import com.alibaba.graphscope.groot.sdk.schema.VertexLabel; +import com.alibaba.graphscope.groot.service.models.BaseEdgeTypeVertexTypePairRelationsInner; +import com.alibaba.graphscope.groot.service.models.DateType; +import com.alibaba.graphscope.groot.service.models.DeleteEdgeRequest; +import com.alibaba.graphscope.groot.service.models.DeleteVertexRequest; +import com.alibaba.graphscope.groot.service.models.EdgeRequest; +import com.alibaba.graphscope.groot.service.models.GSDataType; +import com.alibaba.graphscope.groot.service.models.GetEdgeType; +import com.alibaba.graphscope.groot.service.models.GetPropertyMeta; +import com.alibaba.graphscope.groot.service.models.GetVertexType; +import com.alibaba.graphscope.groot.service.models.PrimitiveType; +import com.alibaba.graphscope.groot.service.models.Property; +import com.alibaba.graphscope.groot.service.models.StringType; +import com.alibaba.graphscope.groot.service.models.TemporalType; +import com.alibaba.graphscope.groot.service.models.TemporalTypeAllOfTemporal; +import com.alibaba.graphscope.groot.service.models.TimeStampType; +import com.alibaba.graphscope.groot.service.models.VertexRequest; +import com.alibaba.graphscope.proto.groot.DataTypePb; + +import org.openapitools.jackson.nullable.JsonNullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class DtoConverter { + public static Vertex convertToVertex(VertexRequest vertexRequest) { + String label = vertexRequest.getLabel(); + Map propertyMap = convertToPropertyMap(vertexRequest.getProperties()); + Map primaryKeyValues = + convertToPropertyMap(vertexRequest.getPrimaryKeyValues()); + propertyMap.putAll(primaryKeyValues); + return new Vertex(label, propertyMap); + } + + public static Vertex convertToVertex(DeleteVertexRequest deleteVertexRequest) { + String label = deleteVertexRequest.getLabel(); + Map primaryKeyValues = + convertToPropertyMap(deleteVertexRequest.getPrimaryKeyValues()); + return new Vertex(label, primaryKeyValues); + } + + public static Edge convertToEdge(EdgeRequest edgeRequest) { + String label = edgeRequest.getEdgeLabel(); + String srcLabel = edgeRequest.getSrcLabel(); + String dstLabel = edgeRequest.getDstLabel(); + Map srcPkMap = convertToPropertyMap(edgeRequest.getSrcPrimaryKeyValues()); + Map dstPkMap = convertToPropertyMap(edgeRequest.getDstPrimaryKeyValues()); + Map propertyMap = convertToPropertyMap(edgeRequest.getProperties()); + return new Edge(label, srcLabel, dstLabel, srcPkMap, dstPkMap, propertyMap); + } + + public static Edge convertToEdge(DeleteEdgeRequest deleteEdgeRequest) { + String label = deleteEdgeRequest.getEdgeLabel(); + String srcLabel = deleteEdgeRequest.getSrcLabel(); + String dstLabel = deleteEdgeRequest.getDstLabel(); + Map srcPkMap = + convertToPropertyMap(deleteEdgeRequest.getSrcPrimaryKeyValues()); + Map dstPkMap = + convertToPropertyMap(deleteEdgeRequest.getDstPrimaryKeyValues()); + return new Edge(label, srcLabel, dstLabel, srcPkMap, dstPkMap); + } + + public static DataTypePb convertToDataTypePb(GSDataType dataType) { + if (dataType instanceof PrimitiveType) { + PrimitiveType primitiveType = (PrimitiveType) dataType; + switch (primitiveType.getPrimitiveType()) { + case SIGNED_INT32: + return DataTypePb.INT; + case UNSIGNED_INT32: + return DataTypePb.UINT; + case SIGNED_INT64: + return DataTypePb.LONG; + case UNSIGNED_INT64: + return DataTypePb.ULONG; + case BOOL: + return DataTypePb.BOOL; + case FLOAT: + return DataTypePb.FLOAT; + case DOUBLE: + return DataTypePb.DOUBLE; + case STRING: + return DataTypePb.STRING; + default: + throw new IllegalArgumentException( + "Unsupported primitive type: " + primitiveType); + } + } else if (dataType instanceof StringType) { + return DataTypePb.STRING; + } else if (dataType instanceof TemporalType) { + TemporalType temporalType = (TemporalType) dataType; + TemporalTypeAllOfTemporal temporal = temporalType.getTemporal(); + if (temporal instanceof DateType) { + return DataTypePb.DATE32; + } else if (temporal instanceof TimeStampType) { + return DataTypePb.TIMESTAMP_MS; + } else { + throw new IllegalArgumentException("Unsupported temporal type: " + temporalType); + } + } + throw new IllegalArgumentException("Unsupported data type: " + dataType); + } + + public static GetVertexType convertToDtoVertexType(VertexLabel vertexLabel) { + GetVertexType vertexType = new GetVertexType(); + vertexType.setTypeName(vertexLabel.getLabel()); + vertexType.setTypeId(vertexLabel.getId()); + vertexType.setProperties(convertToDtoProperties(vertexLabel.getProperties())); + vertexType.setPrimaryKeys( + vertexLabel.getProperties().stream() + .filter(p -> p.isPrimaryKey()) + .map(p -> p.getName()) + .collect(Collectors.toList())); + return vertexType; + } + + public static GetEdgeType convertToDtoEdgeType(EdgeLabel edgeLabel) { + GetEdgeType edgeType = new GetEdgeType(); + edgeType.setTypeName(edgeLabel.getLabel()); + edgeType.setTypeId(edgeLabel.getId()); + edgeType.setProperties(convertToDtoProperties(edgeLabel.getProperties())); + for (EdgeRelation edgeRelation : edgeLabel.getRelations()) { + BaseEdgeTypeVertexTypePairRelationsInner pair = + new BaseEdgeTypeVertexTypePairRelationsInner(); + pair.setSourceVertex(edgeRelation.getSrcLabel()); + pair.setDestinationVertex(edgeRelation.getDstLabel()); + edgeType.addVertexTypePairRelationsItem(pair); + } + return edgeType; + } + + private static Map convertToPropertyMap(List properties) { + Map propertyMap = new HashMap<>(); + for (Property property : properties) { + String value = extractValue(property.getValue()); + propertyMap.put(property.getName(), value); + } + return propertyMap; + } + + private static List convertToDtoProperties( + List properties) { + List propertyMetas = new ArrayList<>(); + for (com.alibaba.graphscope.groot.sdk.schema.Property property : properties) { + GetPropertyMeta propertyMeta = new GetPropertyMeta(); + propertyMeta.setPropertyName(property.getName()); + propertyMeta.setPropertyId(property.getId()); + propertyMeta.setPropertyType(convertToDtoDataType(property.getDataType())); + propertyMetas.add(propertyMeta); + } + return propertyMetas; + } + + private static GSDataType convertToDtoDataType(DataTypePb dataType) { + switch (dataType) { + case INT: + return new PrimitiveType( + PrimitiveType.PrimitiveTypeEnum.SIGNED_INT32, "PrimitiveType"); + case UINT: + return new PrimitiveType( + PrimitiveType.PrimitiveTypeEnum.UNSIGNED_INT32, "PrimitiveType"); + case LONG: + return new PrimitiveType( + PrimitiveType.PrimitiveTypeEnum.SIGNED_INT64, "PrimitiveType"); + case ULONG: + return new PrimitiveType( + PrimitiveType.PrimitiveTypeEnum.UNSIGNED_INT64, "PrimitiveType"); + case BOOL: + return new PrimitiveType(PrimitiveType.PrimitiveTypeEnum.BOOL, "PrimitiveType"); + case FLOAT: + return new PrimitiveType(PrimitiveType.PrimitiveTypeEnum.FLOAT, "PrimitiveType"); + case DOUBLE: + return new PrimitiveType(PrimitiveType.PrimitiveTypeEnum.DOUBLE, "PrimitiveType"); + case STRING: + return new PrimitiveType(PrimitiveType.PrimitiveTypeEnum.STRING, "PrimitiveType"); + case DATE32: + TemporalTypeAllOfTemporal date = new DateType("YYYY-MM-DD".toString(), "DateType"); + return new TemporalType(date, "TemporalType"); + case TIMESTAMP_MS: + // TODO: confirm the format of timestamp? should be int64 milliseconds since + // 1970-01-01 00:00:00.000000? + TemporalTypeAllOfTemporal timestamp = + new TimeStampType("YYYY-MM-DD HH:MM:SS".toString(), "TimeStampType"); + return new TemporalType(timestamp, "TemporalType"); + default: + throw new IllegalArgumentException("Unsupported data type: " + dataType); + } + } + + private static String extractValue(JsonNullable jsonNullable) { + return jsonNullable.isPresent() ? jsonNullable.get().toString() : null; + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/EdgeManagementService.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/EdgeManagementService.java new file mode 100644 index 000000000000..97b3d0b79076 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/EdgeManagementService.java @@ -0,0 +1,72 @@ +package com.alibaba.graphscope.groot.service.impl; + +import com.alibaba.graphscope.groot.sdk.GrootClient; +import com.alibaba.graphscope.groot.sdk.schema.Edge; +import com.alibaba.graphscope.groot.service.models.DeleteEdgeRequest; +import com.alibaba.graphscope.groot.service.models.EdgeRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class EdgeManagementService { + private final GrootClient grootClient; + + @Autowired + public EdgeManagementService(GrootClient grootClient) { + this.grootClient = grootClient; + } + + public long addEdge(EdgeRequest edgeRequest) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + return grootClient.addEdge(edge); + } + + public long addEdges(List edgeRequests) { + if (edgeRequests.size() == 1) { + return addEdge(edgeRequests.get(0)); + } else { + List edges = new ArrayList<>(); + for (EdgeRequest edgeRequest : edgeRequests) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + edges.add(edge); + } + return grootClient.addEdges(edges); + } + } + + public long deleteEdge(DeleteEdgeRequest deleteEdgeRequest) { + Edge edge = DtoConverter.convertToEdge(deleteEdgeRequest); + // TODO: deleteEdge will only delete the first edge that matches the given parameters + // e.g., if we have multiple edges from v1 to v2 with the same edge label, only one of them + // will be deleted + return grootClient.deleteEdge(edge); + } + + public long deleteEdges(List edgeRequests) { + List edges = new ArrayList<>(); + for (DeleteEdgeRequest edgeRequest : edgeRequests) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + edges.add(edge); + } + return grootClient.deleteEdges(edges); + } + + public long updateEdge(EdgeRequest edgeRequest) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + // TODO: updateEdge will add a new edge even if it already exists + return grootClient.updateEdge(edge); + } + + public long updateEdges(List edgeRequests) { + List edges = new ArrayList<>(); + for (EdgeRequest edgeRequest : edgeRequests) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + edges.add(edge); + } + return grootClient.updateEdges(edges); + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/GrootClientConfig.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/GrootClientConfig.java new file mode 100644 index 000000000000..64928c93dd43 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/GrootClientConfig.java @@ -0,0 +1,14 @@ +package com.alibaba.graphscope.groot.service.impl; + +import com.alibaba.graphscope.groot.sdk.GrootClient; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GrootClientConfig { + @Bean + public GrootClient grootClient() { + return GrootClient.newBuilder().addHost("localhost", 55556).build(); + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/SchemaManagementService.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/SchemaManagementService.java new file mode 100644 index 000000000000..438dec4a9e74 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/SchemaManagementService.java @@ -0,0 +1,149 @@ +package com.alibaba.graphscope.groot.service.impl; + +import com.alibaba.graphscope.groot.sdk.GrootClient; +import com.alibaba.graphscope.groot.sdk.schema.EdgeLabel; +import com.alibaba.graphscope.groot.sdk.schema.Property; +import com.alibaba.graphscope.groot.sdk.schema.Schema; +import com.alibaba.graphscope.groot.sdk.schema.VertexLabel; +import com.alibaba.graphscope.groot.service.models.BaseEdgeTypeVertexTypePairRelationsInner; +import com.alibaba.graphscope.groot.service.models.CreateEdgeType; +import com.alibaba.graphscope.groot.service.models.CreateGraphSchemaRequest; +import com.alibaba.graphscope.groot.service.models.CreatePropertyMeta; +import com.alibaba.graphscope.groot.service.models.CreateVertexType; +import com.alibaba.graphscope.groot.service.models.GetGraphSchemaResponse; +import com.alibaba.graphscope.proto.GraphDefPb; +import com.alibaba.graphscope.proto.groot.DataTypePb; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class SchemaManagementService { + private final GrootClient grootClient; + + @Autowired + public SchemaManagementService(GrootClient grootClient) { + this.grootClient = grootClient; + } + + public void createVertexType(CreateVertexType createVertexType) { + VertexLabel vLabelBuilder = createVertexLabel(createVertexType); + Schema.Builder schema = Schema.newBuilder(); + schema.addVertexLabel(vLabelBuilder); + grootClient.submitSchema(schema); + } + + public void deleteVertexType(String vertexType) { + VertexLabel.Builder vLabelBuilder = VertexLabel.newBuilder().setLabel(vertexType); + Schema.Builder schema = Schema.newBuilder(); + schema.dropVertexLabel(vLabelBuilder); + grootClient.submitSchema(schema); + } + + public void updateVertexType(CreateVertexType updateVertexType) { + VertexLabel vLabel = createVertexLabel(updateVertexType); + Schema.Builder schema = Schema.newBuilder(); + schema.addVertexLabelProperties(vLabel); + grootClient.submitSchema(schema); + } + + public void createEdgeType(CreateEdgeType createEdgeType) { + EdgeLabel eLabel = createEdgeLabel(createEdgeType); + Schema.Builder schema = Schema.newBuilder(); + schema.addEdgeLabel(eLabel); + grootClient.submitSchema(schema); + } + + public void deleteEdgeType(String edgeType, String srcVertexType, String dstVertexType) { + // Delete edge relation, if it is the only relation, then delete edge label + EdgeLabel.Builder eKindBuilder = EdgeLabel.newBuilder(); + eKindBuilder.setLabel(edgeType); + eKindBuilder.addRelation(srcVertexType, dstVertexType); + Schema.Builder schema = Schema.newBuilder(); + schema.dropEdgeLabelOrKind(eKindBuilder); + GraphDefPb grootGraphDefPb = grootClient.submitSchema(schema); + if (grootGraphDefPb.getEdgeKindsList().stream() + .noneMatch(edgeKind -> edgeKind.getEdgeLabel().equals(edgeType))) { + // no more edgeKind with the given edge label, so we can delete it. + EdgeLabel.Builder eLabelBuilder = EdgeLabel.newBuilder(); + eLabelBuilder.setLabel(edgeType); + Schema.Builder schema2 = Schema.newBuilder(); + schema2.dropEdgeLabelOrKind(eLabelBuilder); + grootClient.submitSchema(schema2); + } + } + + public void updateEdgeType(CreateEdgeType updateEdgeType) { + EdgeLabel eLabel = createEdgeLabel(updateEdgeType); + Schema.Builder schema = Schema.newBuilder(); + schema.addEdgeLabelProperties(eLabel); + grootClient.submitSchema(schema); + } + + public void importSchema(CreateGraphSchemaRequest createSchema) { + Schema.Builder schema = Schema.newBuilder(); + for (CreateVertexType vertexType : createSchema.getVertexTypes()) { + VertexLabel vLabel = createVertexLabel(vertexType); + schema.addVertexLabel(vLabel); + } + for (CreateEdgeType edgeType : createSchema.getEdgeTypes()) { + EdgeLabel eLabel = createEdgeLabel(edgeType); + schema.addEdgeLabel(eLabel); + } + grootClient.submitSchema(schema); + } + + public GetGraphSchemaResponse getSchema() { + Schema schema = Schema.fromGraphDef(grootClient.getSchema()); + GetGraphSchemaResponse response = new GetGraphSchemaResponse(); + for (VertexLabel vertex : schema.getVertexLabels()) { + response.addVertexTypesItem(DtoConverter.convertToDtoVertexType(vertex)); + } + for (EdgeLabel edge : schema.getEdgeLabels()) { + response.addEdgeTypesItem(DtoConverter.convertToDtoEdgeType(edge)); + } + return response; + } + + public void dropSchema() { + grootClient.dropSchema(); + } + + private VertexLabel createVertexLabel(CreateVertexType createVertexType) { + VertexLabel.Builder vLabelBuilder = VertexLabel.newBuilder(); + vLabelBuilder.setLabel(createVertexType.getTypeName()); + List primaryKeys = createVertexType.getPrimaryKeys(); + for (CreatePropertyMeta prop : createVertexType.getProperties()) { + DataTypePb dataType = DtoConverter.convertToDataTypePb(prop.getPropertyType()); + Property.Builder property = + Property.newBuilder().setName(prop.getPropertyName()).setDataType(dataType); + if (primaryKeys.contains(prop.getPropertyName())) { + property.setPrimaryKey(); + } + vLabelBuilder.addProperty(property); + } + return vLabelBuilder.build(); + } + + private EdgeLabel createEdgeLabel(CreateEdgeType createEdgeType) { + EdgeLabel.Builder eLabelBuilder = EdgeLabel.newBuilder(); + eLabelBuilder.setLabel(createEdgeType.getTypeName()); + for (BaseEdgeTypeVertexTypePairRelationsInner pair : + createEdgeType.getVertexTypePairRelations()) { + eLabelBuilder.addRelation(pair.getSourceVertex(), pair.getDestinationVertex()); + } + for (CreatePropertyMeta prop : createEdgeType.getProperties()) { + DataTypePb dataType = DtoConverter.convertToDataTypePb(prop.getPropertyType()); + Property.Builder property = + Property.newBuilder().setName(prop.getPropertyName()).setDataType(dataType); + eLabelBuilder.addProperty(property); + } + return eLabelBuilder.build(); + } + + public boolean remoteFlush(long snapshotId) { + return grootClient.remoteFlush(snapshotId); + } +} diff --git a/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/VertexManagementService.java b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/VertexManagementService.java new file mode 100644 index 000000000000..7d82e949a2c3 --- /dev/null +++ b/interactive_engine/groot-http/src/main/java/com/alibaba/graphscope/groot/service/impl/VertexManagementService.java @@ -0,0 +1,82 @@ +package com.alibaba.graphscope.groot.service.impl; + +import com.alibaba.graphscope.groot.sdk.GrootClient; +import com.alibaba.graphscope.groot.sdk.schema.Edge; +import com.alibaba.graphscope.groot.sdk.schema.Vertex; +import com.alibaba.graphscope.groot.service.models.DeleteVertexRequest; +import com.alibaba.graphscope.groot.service.models.EdgeRequest; +import com.alibaba.graphscope.groot.service.models.VertexEdgeRequest; +import com.alibaba.graphscope.groot.service.models.VertexRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class VertexManagementService { + + private final GrootClient grootClient; + + @Autowired + public VertexManagementService(GrootClient grootClient) { + this.grootClient = grootClient; + } + + public long addVertex(VertexRequest vertexRequest) { + Vertex vertex = DtoConverter.convertToVertex(vertexRequest); + return grootClient.addVertex(vertex); + } + + public long addVertices(List vertexRequests) { + List vertices = new ArrayList<>(); + for (VertexRequest vertexRequest : vertexRequests) { + Vertex vertex = DtoConverter.convertToVertex(vertexRequest); + vertices.add(vertex); + } + return grootClient.addVertices(vertices); + } + + public long deleteVertex(DeleteVertexRequest deleteVertexRequest) { + Vertex vertex = DtoConverter.convertToVertex(deleteVertexRequest); + return grootClient.deleteVertex(vertex); + } + + public long deleteVertices(List deleteVertexRequests) { + List vertices = new ArrayList<>(); + for (DeleteVertexRequest deleteVertexRequest : deleteVertexRequests) { + Vertex vertex = DtoConverter.convertToVertex(deleteVertexRequest); + vertices.add(vertex); + } + return grootClient.deleteVertices(vertices); + } + + public long updateVertex(VertexRequest vertexRequest) { + Vertex vertex = DtoConverter.convertToVertex(vertexRequest); + return grootClient.updateVertex(vertex); + } + + public long updateVertices(List vertexRequests) { + List vertices = new ArrayList<>(); + for (VertexRequest vertexRequest : vertexRequests) { + Vertex vertex = DtoConverter.convertToVertex(vertexRequest); + vertices.add(vertex); + } + return grootClient.updateVertices(vertices); + } + + public long addVerticesAndEdges(VertexEdgeRequest vertexEdgeRequest) { + List vertices = new ArrayList<>(); + for (VertexRequest vertexRequest : vertexEdgeRequest.getVertexRequest()) { + Vertex vertex = DtoConverter.convertToVertex(vertexRequest); + vertices.add(vertex); + } + List edges = new ArrayList<>(); + for (EdgeRequest edgeRequest : vertexEdgeRequest.getEdgeRequest()) { + Edge edge = DtoConverter.convertToEdge(edgeRequest); + edges.add(edge); + } + return grootClient.addVerticesAndEdges(vertices, edges); + } +} diff --git a/interactive_engine/groot-http/src/test/java/com/alibaba/graphscope/groot/OpenApiGeneratorApplicationTests.java b/interactive_engine/groot-http/src/test/java/com/alibaba/graphscope/groot/OpenApiGeneratorApplicationTests.java new file mode 100644 index 000000000000..ac38704affd7 --- /dev/null +++ b/interactive_engine/groot-http/src/test/java/com/alibaba/graphscope/groot/OpenApiGeneratorApplicationTests.java @@ -0,0 +1,11 @@ +package com.alibaba.graphscope.groot; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class OpenApiGeneratorApplicationTests { + + @Test + void contextLoads() {} +} diff --git a/interactive_engine/pom.xml b/interactive_engine/pom.xml index e5f55ca86aec..335c19c32367 100644 --- a/interactive_engine/pom.xml +++ b/interactive_engine/pom.xml @@ -80,6 +80,7 @@ groot-module groot-server groot-client + groot-http executor/engine/pegasus/clients/java/client compiler @@ -255,6 +256,8 @@ 4.4.0 0.4.3 no-gaia-ir + + 2.7.15 @@ -698,6 +701,26 @@ ${interactive.sdk.version} ${interactive.sdk.classifier} + + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + import + pom + @@ -885,6 +908,11 @@ + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} +