diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/hugegraph-python/README.md b/hugegraph-python/README.md new file mode 100644 index 000000000..42106473f --- /dev/null +++ b/hugegraph-python/README.md @@ -0,0 +1,62 @@ +# hugegraph-python +A Python SDK for Apache HugeGraph + +# Installation + +```shell +pip3 install hugegraph-python +``` + +## Install from source +release soon + +# Examples + +```python +from src.client import PyHugeClient + +client = PyHugeClient("127.0.0.1", "8080", user="admin", pwd="admin", graph="hugegraph") + +"""system""" +print(client.get_graphinfo()) +print(client.get_all_graphs()) +print(client.get_version()) +print(client.get_graph_config()) + +"""schema""" +schema = client.schema() +schema.propertyKey("name").asText().ifNotExist().create() +schema.propertyKey("birthDate").asText().ifNotExist().create() +schema.vertexLabel("Person").properties("name", "birthDate").usePrimaryKeyId().primaryKeys( + "name").ifNotExist().create() +schema.vertexLabel("Movie").properties("name").usePrimaryKeyId().primaryKeys("name").ifNotExist().create() +schema.edgeLabel("ActedIn").sourceLabel("Person").targetLabel("Movie").ifNotExist().create() + +print(schema.getVertexLabels()) +print(schema.getEdgeLabels()) +print(schema.getRelations()) + +"""graph""" +g = client.graph() +g.addVertex("Person", {"name": "Al Pacino", "birthDate": "1940-04-25"}) +g.addVertex("Person", {"name": "Robert De Niro", "birthDate": "1943-08-17"}) +g.addVertex("Movie", {"name": "The Godfather"}) +g.addVertex("Movie", {"name": "The Godfather Part II"}) +g.addVertex("Movie", {"name": "The Godfather Coda The Death of Michael Corleone"}) + +g.addEdge("ActedIn", "12:Al Pacino", "13:The Godfather", {}) +g.addEdge("ActedIn", "12:Al Pacino", "13:The Godfather Part II", {}) +g.addEdge("ActedIn", "12:Al Pacino", "13:The Godfather Coda The Death of Michael Corleone", {}) +g.addEdge("ActedIn", "12:Robert De Niro", "13:The Godfather Part II", {}) + +res = g.getVertexById("12:Al Pacino").label +# g.removeVertexById("12:Al Pacino") +print(res) +g.close() + +"""gremlin""" +g = client.gremlin() +res = g.exec("g.V().limit(10)") +print(res) + +``` \ No newline at end of file diff --git a/hugegraph-python/example/__init__.py b/hugegraph-python/example/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/example/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/example/client_test.py b/hugegraph-python/example/client_test.py new file mode 100644 index 000000000..c3d980814 --- /dev/null +++ b/hugegraph-python/example/client_test.py @@ -0,0 +1,23 @@ +import unittest +from typing import Any +from unittest.mock import MagicMock, patch + +from example.test_hugegraph import HugeGraph + +class HugeGraphTest(unittest.TestCase): + def setUp(self) -> None: + self.username = "test_user" + self.password = "test_password" + self.address = "test_address" + self.graph = "test_hugegraph" + self.port = 1234 + self.session_pool_size = 10 + + @patch("src.client.PyHugeGraph") + def test_init(self, mock_graph: Any) -> None: + mock_graph.return_value = MagicMock() + client = HugeGraph(self.username, self.password, self.address, self.port, self.graph) + + result = client.exec("g.V().limit(10)") + self.assertIsInstance(result, MagicMock) + diff --git a/hugegraph-python/example/hugegraph_test.py b/hugegraph-python/example/hugegraph_test.py new file mode 100644 index 000000000..e41908aaf --- /dev/null +++ b/hugegraph-python/example/hugegraph_test.py @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +class HugeGraph: + """HugeGraph wrapper for graph operations""" + + def __init__( + self, + username: str = "default", + password: str = "default", + address: str = "127.0.0.1", + port: int = 8081, + graph: str = "hugegraph" + ) -> None: + """Create a new HugeGraph wrapper instance.""" + try: + from src.client import PyHugeClient + except ImportError: + raise ValueError( + "Please install HugeGraph Python client first: " + "`pip3 install hugegraph-python-client`" + ) + + self.username = username + self.password = password + self.address = address + self.port = port + self.graph = graph + self.client = PyHugeClient(address, port, user=username, pwd=password, graph=graph) + self.schema = "" + + def exec(self, query) -> str: + """Returns the schema of the HugeGraph database""" + return self.client.gremlin().exec(query) diff --git a/hugegraph-python/example/test.py b/hugegraph-python/example/test.py new file mode 100644 index 000000000..056ccae8a --- /dev/null +++ b/hugegraph-python/example/test.py @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from src.client import PyHugeClient + +if __name__ == '__main__': + client = PyHugeClient("127.0.0.1", "8080", user="admin", pwd="admin", graph="test") + + """schema""" + schema = client.schema() + schema.propertyKey("name").asText().ifNotExist().create() + schema.propertyKey("birthDate").asText().ifNotExist().create() + schema.vertexLabel("Person").properties("name", "birthDate").usePrimaryKeyId().primaryKeys( + "name").ifNotExist().create() + schema.vertexLabel("Movie").properties("name").usePrimaryKeyId().primaryKeys("name").ifNotExist().create() + schema.edgeLabel("ActedIn").sourceLabel("Person").targetLabel("Movie").ifNotExist().create() + + print(schema.getVertexLabels()) + print(schema.getEdgeLabels()) + print(schema.getRelations()) + + """graph""" + g = client.graph() + # add Vertex + p1 = g.addVertex("Person", {"name": "Al Pacino", "birthDate": "1940-04-25"}) + p2 = g.addVertex("Person", {"name": "Robert De Niro", "birthDate": "1943-08-17"}) + m1 = g.addVertex("Movie", {"name": "The Godfather"}) + m2 = g.addVertex("Movie", {"name": "The Godfather Part II"}) + m3 = g.addVertex("Movie", {"name": "The Godfather Coda The Death of Michael Corleone"}) + + # add Edge + g.addEdge("ActedIn", p1.id, m1.id, {}) + g.addEdge("ActedIn", p1.id, m2.id, {}) + g.addEdge("ActedIn", p1.id, m3.id, {}) + g.addEdge("ActedIn", p2.id, m2.id, {}) + + # update property + g.eliminateVertex("vertex_id", {"property_key": "property_value"}) + + + print(g.getVertexById(p1.id).label) + # g.removeVertexById("12:Al Pacino") + g.close() + + """gremlin""" + g = client.gremlin() + print("gremlin.exec: ", g.exec("g.V().limit(10)")) + + """graphs""" + g = client.graphs() + print("get_graph_info: ", g.get_graph_info()) + print("get_all_graphs: ", g.get_all_graphs()) + print("get_version: ", g.get_version()) + print("get_graph_config: ", g.get_graph_config()) diff --git a/hugegraph-python/requirements.txt b/hugegraph-python/requirements.txt new file mode 100644 index 000000000..d9f0102b2 --- /dev/null +++ b/hugegraph-python/requirements.txt @@ -0,0 +1,4 @@ +decorator==5.1.1 +Requests==2.31.0 +setuptools==67.6.1 +urllib3==2.0.3 diff --git a/hugegraph-python/src/__init__.py b/hugegraph-python/src/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/src/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/src/api/__init__.py b/hugegraph-python/src/api/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/src/api/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/src/api/common.py b/hugegraph-python/src/api/common.py new file mode 100644 index 000000000..42b6c0aea --- /dev/null +++ b/hugegraph-python/src/api/common.py @@ -0,0 +1,67 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from src.utils.constants import Constants + + +class ParameterHolder: + def __init__(self): + self._dic = {} + + def set(self, key, value): + self._dic[key] = value + + def get_value(self, key): + if key not in self._dic: + return None + else: + return self._dic[key] + + def get_dic(self): + return self._dic + + def get_keys(self): + return self._dic.keys() + + +class HugeParamsBase: + def __init__(self, graph_instance): + self._graph_instance = graph_instance + self._ip = graph_instance.ip + self._port = graph_instance.port + self._user = graph_instance.user_name + self._pwd = graph_instance.passwd + self._host = "http://{}:{}".format(graph_instance.ip, graph_instance.port) + self._auth = (graph_instance.user_name, graph_instance.passwd) + self._graph_name = graph_instance.graph_name + self._parameter_holder = None + self._headers = { + 'Content-Type': Constants.HEADER_CONTENT_TYPE + } + self._timeout = graph_instance.timeout + + def add_parameter(self, key, value): + self._parameter_holder.set(key, value) + return + + def get_parameter_holder(self): + return self._parameter_holder + + def create_parameter_holder(self): + self._parameter_holder = ParameterHolder() + + def clean_parameter_holder(self): + self._parameter_holder = None diff --git a/hugegraph-python/src/api/graph.py b/hugegraph-python/src/api/graph.py new file mode 100644 index 000000000..1ddb594b3 --- /dev/null +++ b/hugegraph-python/src/api/graph.py @@ -0,0 +1,276 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +from src.utils.huge_requests import HugeSession +from src.api.common import HugeParamsBase +from src.structure.vertex_data import VertexData +from src.structure.edge_data import EdgeData +from src.utils.exceptions import NotFoundError, CreateError, RemoveError, UpdateError +from src.utils.util import create_exception, check_if_authorized, check_if_success + + +class GraphManager(HugeParamsBase): + def __init__(self, graph_instance): + super().__init__(graph_instance) + self.session = self.set_session(HugeSession.new_session()) + + def set_session(self, session): + self.session = session + return session + + def close(self): + if self.session: + self.session.close() + + def addVertex(self, label, properties, id=None): + data = dict() + if id is not None: + data['id'] = id + data['label'] = label + data["properties"] = properties + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices' + response = self.session.post(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, CreateError("create vertex failed: {}".format(response.content))): + res = VertexData(json.loads(response.content)) + return res + + def addVertices(self, input_data): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices/batch' + + data = [] + for item in input_data: + data.append({'label': item[0], 'properties': item[1]}) + response = self.session.post(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, CreateError("create vertexes failed: {}".format(response.content))): + res = [] + for item in json.loads(response.content): + res.append(VertexData({"id": item})) + return res + + def appendVertex(self, vertex_id, properties): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"?action=append' + + data = { + "properties": properties + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, UpdateError("append vertex failed: {}".format(response.content))): + res = VertexData(json.loads(response.content)) + return res + + def eliminateVertex(self, vertex_id, properties): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"?action=eliminate' + + data = { + "properties": properties + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers, + timeout=self._timeout) + if check_if_success(response, UpdateError("eliminate vertex failed: {}".format(response.content))): + res = VertexData(json.loads(response.content)) + return res + + def getVertexById(self, vertex_id): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"' + + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, NotFoundError("Vertex not found: {}".format(response.content))): + res = VertexData(json.loads(response.content)) + return res + + def getVertexByPage(self, label, limit, page, properties=None): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices?' + + para = "" + para = para + "&label=" + label + if properties: + para = para + "&properties=" + json.dumps(properties) + if page: + para += '&page={}'.format(page) + else: + para += '&page' + para = para + "&limit=" + str(limit) + url = url + para[1:] + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, NotFoundError("Vertex not found: {}".format(response.content))): + res = [] + for item in json.loads(response.content)["vertices"]: + res.append(VertexData(item)) + next_page = json.loads(response.content)["page"] + return res, next_page + + def getVertexByCondition(self, label="", limit=0, page='', properties=None): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices?' + + para = "" + if label: + para = para + "&label=" + label + if properties: + para = para + "&properties=" + json.dumps(properties) + if limit > 0: + para = para + "&limit=" + str(limit) + if page: + para += '&page={}'.format(page) + else: + para += '&page' + url = url + para[1:] + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, NotFoundError("Vertex not found: {}".format(response.content))): + res = [] + for item in json.loads(response.content)["vertices"]: + res.append(VertexData(item)) + return res + + def removeVertexById(self, vertex_id): + url = f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"' + response = self.session.delete(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, RemoveError("remove vertex failed: {}".format(response.content))): + return response.content + + def addEdge(self, edge_label, out_id, in_id, properties): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges' + + data = { + "label": edge_label, + "outV": out_id, + "inV": in_id, + "properties": properties + } + response = self.session.post(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, CreateError("created edge failed: {}".format(response.content))): + res = EdgeData(json.loads(response.content)) + return res + + def addEdges(self, input_data): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges/batch' + + data = [] + for item in input_data: + data.append({'label': item[0], 'outV': item[1], 'inV': item[2], 'outVLabel': item[3], + 'inVLabel': item[4], 'properties': item[5]}) + response = self.session.post(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, CreateError("created edges failed: {}".format(response.content))): + res = [] + for item in json.loads(response.content): + res.append(EdgeData({"id": item})) + return res + + def appendEdge(self, edge_id, properties): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}?action=append' + + data = { + "properties": properties + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, UpdateError("append edge failed: {}".format(response.content))): + res = EdgeData(json.loads(response.content)) + return res + + def eliminateEdge(self, edge_id, properties): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}?action=eliminate' + + data = { + "properties": properties + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, + headers=self._headers, timeout=self._timeout) + if check_if_success(response, UpdateError("eliminate edge failed: {}".format(response.content))): + res = EdgeData(json.loads(response.content)) + return res + + def getEdgeById(self, edge_id): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}' + + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, NotFoundError("not found edge: {}".format(response.content))): + res = EdgeData(json.loads(response.content)) + return res + + def getEdgeByPage(self, label=None, vertex_id=None, direction=None, limit=0, page=None, properties=None): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges?' + + para = "" + if vertex_id: + if direction: + para = para + "&vertex_id=\"" + vertex_id + "\"&direction=" + direction + else: + raise NotFoundError("Direction can not be empty.") + if label: + para = para + "&label=" + label + if properties: + para = para + "&properties=" + json.dumps(properties) + if page is not None: + if page: + para += '&page={}'.format(page) + else: + para += '&page' + if limit > 0: + para = para + "&limit=" + str(limit) + url = url + para[1:] + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, NotFoundError("not found edges: {}".format(response.content))): + res = [] + for item in json.loads(response.content)["edges"]: + res.append(EdgeData(item)) + return res, json.loads(response.content)["page"] + + def removeEdgeById(self, edge_id): + url = f'{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}' + + response = self.session.delete(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if check_if_success(response, RemoveError("remove edge failed: {}".format(response.content))): + return response.content + + def getVerticesById(self, vertex_ids): + if not vertex_ids: + return [] + url = f'{self._host}/graphs/{self._graph_name}/traversers/vertices?' + for vertex_id in vertex_ids: + url += 'ids="{}"&'.format(vertex_id) + url = url.rstrip("&") + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if response.status_code == 200 and check_if_authorized(response): + res = [] + for item in json.loads(response.content)["vertices"]: + res.append(VertexData(item)) + return res + else: + create_exception(response.content) + + def getEdgesById(self, edge_ids): + if not edge_ids: + return [] + url = f'{self._host}/graphs/{self._graph_name}/traversers/edges?' + for vertex_id in edge_ids: + url += 'ids={}&'.format(vertex_id) + url = url.rstrip("&") + response = self.session.get(url, auth=self._auth, headers=self._headers, timeout=self._timeout) + if response.status_code == 200 and check_if_authorized(response): + res = [] + for item in json.loads(response.content)["edges"]: + res.append(EdgeData(item)) + return res + else: + create_exception(response.content) diff --git a/hugegraph-python/src/api/graphs.py b/hugegraph-python/src/api/graphs.py new file mode 100644 index 000000000..b22ec3a95 --- /dev/null +++ b/hugegraph-python/src/api/graphs.py @@ -0,0 +1,71 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either expresponses or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import json +import re + +import requests + +from src.api.common import HugeParamsBase + +from src.utils.constants import Constants +from src.utils.exceptions import NotFoundError +from src.utils.huge_requests import HugeSession +from src.utils.util import check_if_success + + +class GraphsManager(HugeParamsBase): + def __init__(self, graph_instance): + super().__init__(graph_instance) + self.session = self.set_session(HugeSession.new_session()) + + def set_session(self, session): + self.session = session + return session + + def close(self): + if self.session: + self.session.close() + + def get_all_graphs(self): + url = f'{self._host}/graphs' + response = self.session.get(url, auth=self._auth, headers=self._headers) + if check_if_success(response, NotFoundError(response.content)): + return str(response.content) + + def get_version(self): + url = f'{self._host}/versions' + response = self.session.get(url, auth=self._auth, headers=self._headers) + if check_if_success(response, NotFoundError(response.content)): + return str(response.content) + + def get_graph_info(self): + url = f'{self._host}/graphs/{self._graph_name}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + if check_if_success(response, NotFoundError(response.content)): + return str(response.content) + + def clear_graph_all_data(self): + url = f'{self._host}/graphs/{self._graph_name}/clear?confirm_message={Constants.CONFORM_MESSAGE}' + response = self.session.delete(url, auth=self._auth, headers=self._headers) + if check_if_success(response, NotFoundError(response.content)): + return str(response.content) + + def get_graph_config(self): + url = self._host + "/graphs" + "/" + self._graph_name + "/conf" + response = self.session.get(url, auth=self._auth, headers=self._headers) + if check_if_success(response, NotFoundError(response.content)): + return str(response.content) diff --git a/hugegraph-python/src/api/gremlin.py b/hugegraph-python/src/api/gremlin.py new file mode 100644 index 000000000..38a7c3056 --- /dev/null +++ b/hugegraph-python/src/api/gremlin.py @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import json +import re + +import requests + +from src.api.common import HugeParamsBase +from src.structure.respon_data import ResponseData +from src.utils.exceptions import NotFoundError +from src.utils.huge_requests import HugeSession +from src.utils.util import check_if_success + + +class GremlinManager(HugeParamsBase): + def __init__(self, graph_instance): + super().__init__(graph_instance) + self.session = self.set_session(HugeSession.new_session()) + + def set_session(self, session): + self.session = session + return session + + def close(self): + if self.session: + self.session.close() + + def exec(self, gremlin): + gremlin = re.sub("^g", self._graph_name + ".traversal()", gremlin) + url = f'{self._host}/gremlin?gremlin={gremlin}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError("Gremlin can't get results: {}".format(response.content)) + if check_if_success(response, error): + return ResponseData(json.loads(response.content)).result diff --git a/hugegraph-python/src/api/schema.py b/hugegraph-python/src/api/schema.py new file mode 100644 index 000000000..9001028f3 --- /dev/null +++ b/hugegraph-python/src/api/schema.py @@ -0,0 +1,166 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +import json + +from src.api.common import HugeParamsBase +from src.api.schema_manage.edge_label import EdgeLabel +from src.api.schema_manage.index_label import IndexLabel +from src.api.schema_manage.property_key import PropertyKey +from src.api.schema_manage.vertex_label import VertexLabel +from src.structure.edge_label_data import EdgeLabelData +from src.structure.index_label_data import IndexLabelData +from src.structure.property_key_data import PropertyKeyData +from src.structure.vertex_label_data import VertexLabelData +from src.utils.exceptions import NotFoundError +from src.utils.huge_requests import HugeSession +from src.utils.util import check_if_success + + +class SchemaManager(HugeParamsBase): + def __init__(self, graph_instance): + super().__init__(graph_instance) + self.session = self.set_session(HugeSession.new_session()) + + def set_session(self, session): + self.session = session + return session + + def close(self): + if self.session: + self.session.close() + + """ + create schemas, including propertyKey/vertexLabel/edgeLabel/indexLabel + """ + + def propertyKey(self, property_name): + property_key = PropertyKey(self._graph_instance, self.session) + property_key.create_parameter_holder() + property_key.add_parameter("name", property_name) + property_key.add_parameter("not_exist", True) + return property_key + + def vertexLabel(self, vertex_name): + vertex_label = VertexLabel(self._graph_instance, self.session) + vertex_label.create_parameter_holder() + vertex_label.add_parameter("name", vertex_name) + # vertex_label.add_parameter("id_strategy", "AUTOMATIC") + vertex_label.add_parameter("not_exist", True) + return vertex_label + + def edgeLabel(self, name): + edge_label = EdgeLabel(self._graph_instance, self.session) + edge_label.create_parameter_holder() + edge_label.add_parameter("name", name) + edge_label.add_parameter("not_exist", True) + return edge_label + + def indexLabel(self, name): + index_label = IndexLabel(self._graph_instance, self.session) + index_label.create_parameter_holder() + index_label.add_parameter("name", name) + return index_label + + """ + create schemas + """ + def getSchema(self): + url = f'{self._host}/graphs/{self._graph_name}/schema' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError(f"schema not found: {response.content}") + if check_if_success(response, error): + schema = json.loads(response.content) + return schema + + def getPropertyKey(self, property_name): + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{property_name}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError(f"PropertyKey not found: {response.content}") + if check_if_success(response, error): + property_keys_data = PropertyKeyData(json.loads(response.content)) + return property_keys_data + + def getPropertyKeys(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys' + response = self.session.get(url, auth=self._auth, headers=self._headers) + res = [] + if check_if_success(response): + for item in json.loads(response.content)["propertykeys"]: + res.append(PropertyKeyData(item)) + return res + + def getVertexLabel(self, name): + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError("VertexLabel not found: {}".format(response.content)) + if check_if_success(response, error): + res = VertexLabelData(json.loads(response.content)) + return res + + def getVertexLabels(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels' + response = self.session.get(url, auth=self._auth, headers=self._headers) + res = [] + if check_if_success(response): + for item in json.loads(response.content)["vertexlabels"]: + res.append(VertexLabelData(item)) + return res + + def getEdgeLabel(self, label_name): + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{label_name}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError("EdgeLabel not found: {}".format(response.content)) + if check_if_success(response, error): + res = EdgeLabelData(json.loads(response.content)) + return res + + def getEdgeLabels(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels' + response = self.session.get(url, auth=self._auth, headers=self._headers) + res = [] + if check_if_success(response): + for item in json.loads(response.content)["edgelabels"]: + res.append(EdgeLabelData(item)) + return res + + def getRelations(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels' + response = self.session.get(url, auth=self._auth, headers=self._headers) + res = [] + if check_if_success(response): + for item in json.loads(response.content)["edgelabels"]: + res.append(EdgeLabelData(item).relations()) + return res + + def getIndexLabel(self, name): + url = f'{self._host}/graphs/{self._graph_name}/schema/indexlabels/{name}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + error = NotFoundError("EdgeLabel not found: {}".format(response.content)) + if check_if_success(response, error): + res = IndexLabelData(json.loads(response.content)) + return res + + def getIndexLabels(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/indexlabels' + response = self.session.get(url, auth=self._auth, headers=self._headers) + res = [] + if check_if_success(response): + for item in json.loads(response.content)['indexlabels']: + res.append(IndexLabelData(item)) + return res diff --git a/hugegraph-python/src/api/schema_manage/__init__.py b/hugegraph-python/src/api/schema_manage/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/src/api/schema_manage/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/src/api/schema_manage/edge_label.py b/hugegraph-python/src/api/schema_manage/edge_label.py new file mode 100644 index 000000000..31d81d968 --- /dev/null +++ b/hugegraph-python/src/api/schema_manage/edge_label.py @@ -0,0 +1,150 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either expresponses or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +from src.api.common import HugeParamsBase +from src.utils.exceptions import CreateError, UpdateError, RemoveError +from src.utils.huge_decorator import decorator_params, decorator_create +from src.utils.util import check_if_success, check_if_authorized + + +class EdgeLabel(HugeParamsBase): + def __init__(self, graph_instance, session): + super().__init__(graph_instance) + self.session = session + + @decorator_params + def link(self, source_label, target_label): + self._parameter_holder.set("source_label", source_label) + self._parameter_holder.set("target_label", target_label) + return self + + @decorator_params + def sourceLabel(self, source_label): + self._parameter_holder.set("source_label", source_label) + return self + + @decorator_params + def targetLabel(self, target_label): + self._parameter_holder.set("target_label", target_label) + return self + + @decorator_params + def userdata(self, *args): + if not self._parameter_holder.get_value("user_data"): + self._parameter_holder.set('user_data', dict()) + user_data = self._parameter_holder.get_value("user_data") + i = 0 + while i < len(args): + user_data[args[i]] = args[i + 1] + i += 2 + return self + + @decorator_params + def properties(self, *args): + self._parameter_holder.set("properties", list(args)) + return self + + @decorator_params + def singleTime(self): + self._parameter_holder.set("frequency", "SINGLE") + return self + + @decorator_params + def multiTimes(self): + self._parameter_holder.set("frequency", "MULTIPLE") + return self + + @decorator_params + def sortKeys(self, *args): + self._parameter_holder.set("sort_keys", list(args)) + return self + + @decorator_params + def nullableKeys(self, *args): + nullable_keys = set(args) + self._parameter_holder.set("nullable_keys", list(nullable_keys)) + return self + + @decorator_params + def ifNotExist(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{self._parameter_holder.get_value("name")}' + + response = self.session.get(url, auth=self._auth, headers=self._headers) + if response.status_code == 200 and check_if_authorized(response): + self._parameter_holder.set("not_exist", False) + return self + + @decorator_create + def create(self): + dic = self._parameter_holder.get_dic() + data = dict() + keys = ['name', 'source_label', 'target_label', 'nullable_keys', 'properties', + 'enable_label_index', 'sort_keys', 'user_data', 'frequency'] + for key in keys: + if key in dic: + data[key] = dic[key] + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels' + response = self.session.post(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = CreateError('CreateError: "create EdgeLabel failed", Detail: "{}"' + .format(str(response.content))) + if check_if_success(response, error): + return 'create EdgeLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def remove(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{self._parameter_holder.get_value("name")}' + response = self.session.delete(url, auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = RemoveError('RemoveError: "remove EdgeLabel failed", Detail: "{}"' + .format(str(response.content))) + if check_if_success(response, error): + return 'remove EdgeLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def append(self): + dic = self._parameter_holder.get_dic() + data = dict() + keys = ['name', 'nullable_keys', 'properties', 'user_data'] + for key in keys: + if key in dic: + data[key] = dic[key] + + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{data["name"]}?action=append' + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = UpdateError('UpdateError: "append EdgeLabel failed", Detail: "{}"'.format(str(response.content))) + if check_if_success(response, error): + return 'append EdgeLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def eliminate(self): + name = self._parameter_holder.get_value("name") + user_data = self._parameter_holder.get_value("user_data") if \ + self._parameter_holder.get_value("user_data") else {} + url = f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{name}?action=eliminate' + data = { + "name": name, + "user_data": user_data + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = UpdateError('UpdateError: "eliminate EdgeLabel failed", Detail: "{}"'.format(str(response.content))) + if check_if_success(response, error): + return 'eliminate EdgeLabel success, Detail: "{}"'.format(str(response.content)) diff --git a/hugegraph-python/src/api/schema_manage/index_label.py b/hugegraph-python/src/api/schema_manage/index_label.py new file mode 100644 index 000000000..cc93a9103 --- /dev/null +++ b/hugegraph-python/src/api/schema_manage/index_label.py @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +import requests + +from src.api.common import HugeParamsBase +from src.utils.huge_decorator import decorator_params, decorator_create +from src.utils.exceptions import CreateError, RemoveError +from src.utils.util import check_if_authorized, check_if_success + + +class IndexLabel(HugeParamsBase): + def __init__(self, graph_instance, session): + super().__init__(graph_instance) + self.session = session + + @decorator_params + def onV(self, vertex_label): + self._parameter_holder.set("base_value", vertex_label) + self._parameter_holder.set("base_type", "VERTEX_LABEL") + return self + + @decorator_params + def onE(self, edge_label): + self._parameter_holder.set("base_value", edge_label) + self._parameter_holder.set("base_type", "EDGE_LABEL") + return self + + @decorator_params + def by(self, *args): + if "fields" not in self._parameter_holder.get_keys(): + self._parameter_holder.set("fields", set()) + s = self._parameter_holder.get_value("fields") + for item in args: + s.add(item) + return self + + @decorator_params + def secondary(self): + self._parameter_holder.set("index_type", "SECONDARY") + return self + + @decorator_params + def range(self): + self._parameter_holder.set("index_type", "RANGE") + return self + + @decorator_params + def Search(self): + self._parameter_holder.set("index_type", "SEARCH") + return self + + @decorator_params + def ifNotExist(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/indexlabels/{self._parameter_holder.get_value("name")}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + if response.status_code == 200 and check_if_authorized(response): + self._parameter_holder.set("not_exist", False) + return self + + @decorator_create + def create(self): + dic = self._parameter_holder.get_dic() + data = dict() + data["name"] = dic["name"] + data["base_type"] = dic["base_type"] + data["base_value"] = dic["base_value"] + data["index_type"] = dic["index_type"] + data["fields"] = list(dic["fields"]) + url = f'{self._host}/graphs/{self._graph_name}/schema/indexlabels' + response = self.session.post(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = CreateError('CreateError: "create IndexLabel failed", ' + 'Detail "{}"'.format(str(response.content))) + if check_if_success(response, error): + return 'create IndexLabel success, Deatil: "{}"'.format(str(response.content)) + + @decorator_params + def remove(self): + name = self._parameter_holder.get_value("name") + url = f'{self._host}/graphs/{self._graph_name}/schema/indexlabels/{name}' + response = self.session.delete(url, auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = RemoveError('RemoveError: "remove IndexLabel failed", ' + 'Detail "{}"'.format(str(response.content))) + if check_if_success(response, error): + return 'remove IndexLabel success, Deatil: "{}"'.format(str(response.content)) diff --git a/hugegraph-python/src/api/schema_manage/property_key.py b/hugegraph-python/src/api/schema_manage/property_key.py new file mode 100644 index 000000000..6f9cce4ac --- /dev/null +++ b/hugegraph-python/src/api/schema_manage/property_key.py @@ -0,0 +1,168 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either expresponses or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +import requests + +from src.api.common import HugeParamsBase +from src.utils.exceptions import CreateError, UpdateError, RemoveError +from src.utils.huge_decorator import decorator_params, decorator_create +from src.utils.util import check_if_success, check_if_authorized + + +class PropertyKey(HugeParamsBase): + def __init__(self, graph_instance, session): + super().__init__(graph_instance) + self.session = session + + @decorator_params + def asInt(self): + self._parameter_holder.set("data_type", "INT") + return self + + @decorator_params + def asText(self): + self._parameter_holder.set("data_type", "TEXT") + return self + + @decorator_params + def asDouble(self): + self._parameter_holder.set("data_type", "DOUBLE") + return self + + @decorator_params + def asDate(self): + self._parameter_holder.set("data_type", "DATE") + return self + + @decorator_params + def valueSingle(self): + self._parameter_holder.set("cardinality", "SINGLE") + return self + + @decorator_params + def valueList(self): + self._parameter_holder.set("cardinality", "LIST") + return self + + @decorator_params + def valueSet(self): + self._parameter_holder.set("cardinality", "SET") + return self + + @decorator_params + def calcMax(self): + self._parameter_holder.set("aggregate_type", "MAX") + return self + + @decorator_params + def calcMin(self): + self._parameter_holder.set("aggregate_type", "MIN") + return self + + @decorator_params + def calcSum(self): + self._parameter_holder.set("aggregate_type", "SUM") + return self + + @decorator_params + def calcOld(self): + self._parameter_holder.set("aggregate_type", "OLD") + return self + + @decorator_params + def userdata(self, *args): + user_data = self._parameter_holder.get_value("user_data") + if not user_data: + self._parameter_holder.set("user_data", dict()) + user_data = self._parameter_holder.get_value("user_data") + i = 0 + while i < len(args): + user_data[args[i]] = args[i + 1] + i += 2 + return self + + def ifNotExist(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{self._parameter_holder.get_value("name")}' + response = self.session.get(url, auth=self._auth, headers=self._headers) + if response.status_code == 200 and check_if_authorized(response): + self._parameter_holder.set("not_exist", False) + return self + + @decorator_create + def create(self): + dic = self._parameter_holder.get_dic() + property_keys = { + "name": dic["name"] + } + if "data_type" in dic: + property_keys["data_type"] = dic["data_type"] + if "cardinality" in dic: + property_keys["cardinality"] = dic["cardinality"] + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys' + response = self.session.post(url, data=json.dumps(property_keys), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + if check_if_success(response, CreateError( + 'CreateError: "create PropertyKey failed", Detail: {}'.format(response.content))): + return 'create PropertyKey success, Detail: {}'.format(response.content) + + @decorator_params + def append(self): + property_name = self._parameter_holder.get_value("name") + user_data = self._parameter_holder.get_value("user_data") + if not user_data: + user_data = dict() + data = { + "name": property_name, + "user_data": user_data + } + + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{property_name}/?action=append' + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + if check_if_success(response, UpdateError( + 'UpdateError: "append PropertyKey failed", Detail: {}'.format(response.content))): + return 'append PropertyKey success, Detail: {}'.format(response.content) + + @decorator_params + def eliminate(self): + property_name = self._parameter_holder.get_value("name") + user_data = self._parameter_holder.get_value("user_data") + if not user_data: + user_data = dict() + data = { + "name": property_name, + "user_data": user_data + } + + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{property_name}/?action=eliminate' + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = UpdateError(f'UpdateError: "eliminate PropertyKey failed", Detail: {str(response.content)}') + if check_if_success(response, error): + return 'eliminate PropertyKey success, Detail: {}'.format(str(response.content)) + + @decorator_params + def remove(self): + dic = self._parameter_holder.get_dic() + url = f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{dic["name"]}' + response = self.session.delete(url) + self.clean_parameter_holder() + if check_if_success(response, RemoveError( + 'RemoveError: "delete PropertyKey failed", Detail: {}'.format(str(response.content)))): + return 'delete PropertyKey success, Detail: {}'.format(dic["name"]) diff --git a/hugegraph-python/src/api/schema_manage/vertex_label.py b/hugegraph-python/src/api/schema_manage/vertex_label.py new file mode 100644 index 000000000..462fe138c --- /dev/null +++ b/hugegraph-python/src/api/schema_manage/vertex_label.py @@ -0,0 +1,156 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +import requests + +from src.api.common import HugeParamsBase +from src.utils.exceptions import CreateError, UpdateError, RemoveError +from src.utils.huge_decorator import decorator_params, decorator_create +from src.utils.util import check_if_success, check_if_authorized + + +class VertexLabel(HugeParamsBase): + def __init__(self, graph_instance, session): + super().__init__(graph_instance) + self.session = session + + @decorator_params + def useAutomaticId(self): + self._parameter_holder.set("id_strategy", "AUTOMATIC") + return self + + @decorator_params + def useCustomizeStringId(self): + self._parameter_holder.set("id_strategy", "CUSTOMIZE_STRING") + return self + + @decorator_params + def useCustomizeNumberId(self): + self._parameter_holder.set("id_strategy", "CUSTOMIZE_NUMBER") + return self + + @decorator_params + def usePrimaryKeyId(self): + self._parameter_holder.set("id_strategy", "PRIMARY_KEY") + return self + + @decorator_params + def properties(self, *args): + self._parameter_holder.set("properties", list(args)) + return self + + @decorator_params + def primaryKeys(self, *args): + self._parameter_holder.set("primary_keys", list(args)) + return self + + @decorator_params + def nullableKeys(self, *args): + self._parameter_holder.set("nullable_keys", list(args)) + return self + + @decorator_params + def enableLabelIndex(self, flag): + self._parameter_holder.set("enable_label_index", flag) + return self + + @decorator_params + def userdata(self, *args): + if "user_data" not in self._parameter_holder.get_keys(): + self._parameter_holder.set('user_data', dict()) + user_data = self._parameter_holder.get_value('user_data') + i = 0 + while i < len(args): + user_data[args[i]] = args[i + 1] + i += 2 + return self + + def ifNotExist(self): + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{self._parameter_holder.get_value("name")}' + + response = self.session.get(url, auth=self._auth, headers=self._headers) + if response.status_code == 200 and check_if_authorized(response): + self._parameter_holder.set("not_exist", False) + return self + + @decorator_create + def create(self): + dic = self._parameter_holder.get_dic() + key_list = ["name", "id_strategy", "primary_keys", "nullable_keys", "index_labels", + "properties", "enable_label_index", "user_data"] + data = {} + for key in key_list: + if key in dic: + data[key] = dic[key] + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels' + response = self.session.post(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = CreateError('CreateError: "create VertexLabel failed", Detail: "{}"' + .format(str(response.content))) + if check_if_success(response, error): + return 'create VertexLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def append(self): + dic = self._parameter_holder.get_dic() + properties = dic['properties'] if "properties" in dic else [] + nullable_keys = dic['nullable_keys'] if "nullable_keys" in dic else [] + user_data = dic['user_data'] if 'user_data' in dic else {} + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{dic["name"]}?action=append' + + data = { + "name": dic["name"], + "properties": properties, + "nullable_keys": nullable_keys, + "user_data": user_data + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = UpdateError('UpdateError: "append VertexLabel failed", Detail: "{}"'. + format(str(response.content))) + if check_if_success(response, error): + return 'append VertexLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def remove(self): + name = self._parameter_holder.get_value("name") + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}' + response = self.session.delete(url, auth=self._auth, headers=self._headers) + self.clean_parameter_holder() + error = RemoveError('RemoveError: "remove VertexLabel failed", Detail: "{}"'. + format(str(response.content))) + if check_if_success(response, error): + return 'remove VertexLabel success, Detail: "{}"'.format(str(response.content)) + + @decorator_params + def eliminate(self): + name = self._parameter_holder.get_value("name") + url = f'{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}/?action=eliminate' + + dic = self._parameter_holder.get_dic() + user_data = dic['user_data'] if 'user_data' in dic else {} + data = { + "name": self._parameter_holder.get_value("name"), + "user_data": user_data, + } + response = self.session.put(url, data=json.dumps(data), auth=self._auth, headers=self._headers) + error = UpdateError('UpdateError: "eliminate VertexLabel failed", Detail: "{}"'. + format(str(response.content))) + if check_if_success(response, error): + return 'eliminate VertexLabel success, Detail: "{}"'.format(str(response.content)) diff --git a/hugegraph-python/src/client.py b/hugegraph-python/src/client.py new file mode 100644 index 000000000..b039725b8 --- /dev/null +++ b/hugegraph-python/src/client.py @@ -0,0 +1,57 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from src.api.common import HugeParamsBase +from src.api.graph import GraphManager +from src.api.graphs import GraphsManager +from src.api.gremlin import GremlinManager +from src.api.schema import SchemaManager +from src.structure.graph_instance import GraphInstance + + +class PyHugeClient(HugeParamsBase): + def __init__(self, ip, port, graph, user, pwd, timeout=10): + self._graph_instance = GraphInstance(ip, port, graph, user, pwd, timeout) + super().__init__(self._graph_instance) + self._schema = None + self._graph = None + self._graphs = None + self._gremlin = None + + def schema(self): + if self._schema: + return self._schema + self._schema = SchemaManager(self._graph_instance) + return self._schema + + def gremlin(self): + if self._gremlin: + return self._gremlin + self._gremlin = GremlinManager(self._graph_instance) + return self._gremlin + + def graph(self): + if self._graph: + return self._graph + self._graph = GraphManager(self._graph_instance) + return self._graph + + def graphs(self): + if self._graphs: + return self._graphs + self._graphs = GraphsManager(self._graph_instance) + return self._graphs diff --git a/hugegraph-python/src/structure/__init__.py b/hugegraph-python/src/structure/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/src/structure/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/src/structure/edge_data.py b/hugegraph-python/src/structure/edge_data.py new file mode 100644 index 000000000..1269e0a07 --- /dev/null +++ b/hugegraph-python/src/structure/edge_data.py @@ -0,0 +1,64 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class EdgeData: + def __init__(self, dic): + self.__id = dic["id"] + self.__label = dic["label"] if "label" in dic else None + self.__type = dic["type"] if "type" in dic else None + self.__outV = dic["outV"] if "outV" in dic else None + self.__outVLabel = dic["outVLabel"] if "outVLabel" in dic else None + self.__inV = dic["inV"] if "inV" in dic else None + self.__inVLabel = dic["inVLabel"] if "inVLabel" in dic else None + self.__properties = dic["properties"] if "properties" in dic else None + + @property + def id(self): + return self.__id + + @property + def label(self): + return self.__label + + @property + def type(self): + return self.__type + + @property + def outV(self): + return self.__outV + + @property + def outVLabel(self): + return self.__outVLabel + + @property + def inV(self): + return self.__inV + + @property + def inVLabel(self): + return self.__inVLabel + + @property + def properties(self): + return self.__properties + + def __repr__(self): + res = "{}--{}-->{}".format(self.__outV, self.__label, self.__inV) + return res diff --git a/hugegraph-python/src/structure/edge_label_data.py b/hugegraph-python/src/structure/edge_label_data.py new file mode 100644 index 000000000..57cfc582d --- /dev/null +++ b/hugegraph-python/src/structure/edge_label_data.py @@ -0,0 +1,78 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class EdgeLabelData: + def __init__(self, dic): + self.__id = dic["id"] + self.__name = dic["name"] + self.__source_label = dic["source_label"] + self.__target_label = dic["target_label"] + self.__frequency = dic["frequency"] + self.__sort_keys = dic["sort_keys"] + self.__nullable_keys = dic["nullable_keys"] + self.__index_labels = dic["index_labels"] + self.__properties = dic["properties"] + self.__enable_label_index = dic["enable_label_index"] + self.__user_data = dic["user_data"] + + @property + def id(self): + return self.__id + + @property + def name(self): + return self.__name + + @property + def sourceLabel(self): + return self.__source_label + + @property + def targetLabel(self): + return self.__target_label + + @property + def frequency(self): + return self.__frequency + + @property + def sortKeys(self): + return self.__sort_keys + + @property + def properties(self): + return self.__properties + + @property + def nullableKeys(self): + return self.__nullable_keys + + @property + def userdata(self): + return self.__user_data + + def relations(self): + res = "{}--{}-->{}".format( + self.__source_label, self.__name, self.__target_label) + return res + + def __repr__(self): + res = "name: {}, properties: {}".format( + self.__name + , self.__properties) + return res diff --git a/hugegraph-python/src/structure/graph_instance.py b/hugegraph-python/src/structure/graph_instance.py new file mode 100644 index 000000000..34bed439d --- /dev/null +++ b/hugegraph-python/src/structure/graph_instance.py @@ -0,0 +1,54 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class GraphInstance: + def __init__(self, ip, port, graph_name, user_name, passwd, timeout): + self.__ip = ip + self.__port = port + self.__graph_name = graph_name + self.__user_name = user_name + self.__passwd = passwd + self.__timeout = timeout + + @property + def ip(self): + return self.__ip + + @property + def port(self): + return self.__port + + @property + def graph_name(self): + return self.__graph_name + + @property + def user_name(self): + return self.__user_name + + @property + def passwd(self): + return self.__passwd + + @property + def timeout(self): + return self.__timeout + + def __repr__(self): + res = f"ip:{self.ip}, port:{self.port}, graph_name:{self.graph_name}, user_name:{self.user_name}, passwd:{self.passwd}, timeout:{self.timeout}" + return res diff --git a/hugegraph-python/src/structure/index_label_data.py b/hugegraph-python/src/structure/index_label_data.py new file mode 100644 index 000000000..4f602330a --- /dev/null +++ b/hugegraph-python/src/structure/index_label_data.py @@ -0,0 +1,56 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class IndexLabelData: + def __init__(self, dic): + self.__id = dic["id"] if "id" in dic else None + self.__base_type = dic["base_type"] if "base_type" in dic else None + self.__base_value = dic["base_value"] if "base_value" in dic else None + self.__name = dic["name"] if "name" in dic else None + self.__fields = dic["fields"] if "fields" in dic else None + self.__index_type = dic["index_type"] if "index_type" in dic else None + + @property + def id(self): + return self.__id + + @property + def baseType(self): + return self.__base_type + + @property + def baseValue(self): + return self.__base_value + + @property + def name(self): + return self.__name + + @property + def fields(self): + return self.__fields + + @property + def indexType(self): + return self.__index_type + + def __repr__(self): + res = "index_name: {}, base_value: {}, base_type: {}, fields: [], index_type: {}"\ + .format(self.__name, self.__base_value, self.__base_type, self.__fields, + self.__index_type) + return res diff --git a/hugegraph-python/src/structure/property_key_data.py b/hugegraph-python/src/structure/property_key_data.py new file mode 100644 index 000000000..665d6325b --- /dev/null +++ b/hugegraph-python/src/structure/property_key_data.py @@ -0,0 +1,51 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class PropertyKeyData: + def __init__(self, dic): + self.__id = dic["id"] + self.__name = dic["name"] + self.__cardinality = dic["cardinality"] + self.__data_type = dic["data_type"] + self.__user_data = dic["user_data"] + + @property + def id(self): + return self.__id + + @property + def cardinality(self): + return self.__cardinality + + @property + def name(self): + return self.__name + + @property + def dataType(self): + return self.__data_type + + @property + def userdata(self): + return self.__user_data + + def __repr__(self): + res = "name: {}, cardinality: {}, data_type: {}".format( + self.__name, self.__cardinality, self.__data_type + ) + return res diff --git a/hugegraph-python/src/structure/respon_data.py b/hugegraph-python/src/structure/respon_data.py new file mode 100644 index 000000000..cbbbc8180 --- /dev/null +++ b/hugegraph-python/src/structure/respon_data.py @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class ResponseData: + def __init__(self, dic): + self.__id = dic["requestId"] + self.__status = dic["status"] + self.__result = dic["result"] + + @property + def id(self): + return self.__id + + @property + def status(self): + return self.__status + + @property + def result(self): + return self.__result + + def __repr__(self): + res = "id: {}, status: {}, result: {}".format(self.__id, self.__status, self.__result) + return res diff --git a/hugegraph-python/src/structure/vertex_data.py b/hugegraph-python/src/structure/vertex_data.py new file mode 100644 index 000000000..eb358bc98 --- /dev/null +++ b/hugegraph-python/src/structure/vertex_data.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class VertexData: + def __init__(self, dic): + self.__id = dic["id"] + self.__label = dic["label"] if "label" in dic else None + self.__type = dic["type"] if "type" in dic else None + self.__properties = dic["properties"] if "properties" in dic else None + + @property + def id(self): + return self.__id + + @property + def label(self): + return self.__label + + @property + def type(self): + return self.__type + + @property + def properties(self): + return self.__properties + + def __repr__(self): + res = "id: {}, label: {}, type: {}".format(self.__id, self.__label, self.__type) + return res diff --git a/hugegraph-python/src/structure/vertex_label_data.py b/hugegraph-python/src/structure/vertex_label_data.py new file mode 100644 index 000000000..fe2a8b560 --- /dev/null +++ b/hugegraph-python/src/structure/vertex_label_data.py @@ -0,0 +1,62 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class VertexLabelData: + def __init__(self, dic): + self.__id = dic["id"] + self.__name = dic["name"] + self.__id_strategy = dic["id_strategy"] + self.__primary_keys = dic["primary_keys"] + self.__nullable_keys = dic["nullable_keys"] + self.__index_labels = dic["index_labels"] + self.__properties = dic["properties"] + self.__enable_label_index = dic["enable_label_index"] + self.__user_data = dic["user_data"] + + @property + def id(self): + return self.__id + + @property + def name(self): + return self.__name + + @property + def primaryKeys(self): + return self.__primary_keys + + @property + def idStrategy(self): + return self.__id_strategy + + @property + def properties(self): + return self.__properties + + @property + def nullableKeys(self): + return self.__nullable_keys + + @property + def userdata(self): + return self.__user_data + + def __repr__(self): + res = "name: {}, primary_keys: {}, properties: {}".format( + self.__name, self.__primary_keys, self.__properties) + return res diff --git a/hugegraph-python/src/utils/__init__.py b/hugegraph-python/src/utils/__init__.py new file mode 100644 index 000000000..e0533d992 --- /dev/null +++ b/hugegraph-python/src/utils/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/hugegraph-python/src/utils/constants.py b/hugegraph-python/src/utils/constants.py new file mode 100644 index 000000000..27a5b8413 --- /dev/null +++ b/hugegraph-python/src/utils/constants.py @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class Constants(str): + CONFORM_MESSAGE = "I%27m+sure+to+delete+all+data" + HEADER_CONTENT_TYPE = "application/json" diff --git a/hugegraph-python/src/utils/exceptions.py b/hugegraph-python/src/utils/exceptions.py new file mode 100644 index 000000000..7cfb4aaac --- /dev/null +++ b/hugegraph-python/src/utils/exceptions.py @@ -0,0 +1,62 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +class NotAuthorizedError(Exception): + """ + Not Authorized + """ + +class InvalidParameter(Exception): + """ + Parameter setting error + """ + +class NotFoundError(Exception): + """ + no content found + """ + + +class CreateError(Exception): + """ + Failed to create vertex or edge + """ + + +class RemoveError(Exception): + """ + Failed to delete vertex or edge + """ + + +class UpdateError(Exception): + """ + Failed to modify node + """ + + +class DataFormatError(Exception): + """ + Input data format error + """ + + +class ServiceUnavailableException(Exception): + """ + The server is too busy to be available + """ diff --git a/hugegraph-python/src/utils/huge_decorator.py b/hugegraph-python/src/utils/huge_decorator.py new file mode 100644 index 000000000..9077bb810 --- /dev/null +++ b/hugegraph-python/src/utils/huge_decorator.py @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from decorator import decorator + +from src.utils.exceptions import NotAuthorizedError + + +@decorator +def decorator_params(func, *args, **kwargs): + parameter_holder = args[0].get_parameter_holder() + if parameter_holder is None or "name" not in parameter_holder.get_keys(): + print('Parameters required, please set necessary parameters.') + raise + return func(*args, **kwargs) + + +@decorator +def decorator_create(func, *args, **kwargs): + parameter_holder = args[0].get_parameter_holder() + if parameter_holder.get_value('not_exist') is False: + return 'Create failed, "{}" already exists.'.format(parameter_holder.get_value('name')) + return func(*args, **kwargs) + + +@decorator +def decorator_auth(func, *args, **kwargs): + response = args[0] + if response.status_code == 401: + raise NotAuthorizedError("NotAuthorized: {}".format(response.content)) + return func(*args, **kwargs) diff --git a/hugegraph-python/src/utils/huge_requests.py b/hugegraph-python/src/utils/huge_requests.py new file mode 100644 index 000000000..744e8b5a3 --- /dev/null +++ b/hugegraph-python/src/utils/huge_requests.py @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import requests +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry + + +class HugeSession: + @staticmethod + def new_session(): + session = requests.Session() + retry = Retry(connect=5, backoff_factor=1) + adapter = HTTPAdapter(max_retries=retry) + session.mount('http://', adapter) + session.mount('https://', adapter) + session.keep_alive = False + return session diff --git a/hugegraph-python/src/utils/util.py b/hugegraph-python/src/utils/util.py new file mode 100644 index 000000000..9b9424bf3 --- /dev/null +++ b/hugegraph-python/src/utils/util.py @@ -0,0 +1,43 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import json + +from src.utils.exceptions import ServiceUnavailableException, NotAuthorizedError, NotFoundError + + +def create_exception(response_content): + data = json.loads(response_content) + if "ServiceUnavailableException" in data["exception"]: + raise ServiceUnavailableException('ServiceUnavailableException, "message": "{}", "cause": "{}"'. + format(data["message"], data["cause"])) + else: + raise Exception(response_content) + + +def check_if_authorized(response): + if response.status_code == 401: + raise NotAuthorizedError("Please check your username and password. {}".format(response.content)) + return True + + +def check_if_success(response, error=None): + if (not str(response.status_code).startswith("20")) and check_if_authorized(response): + if error is None: + error = NotFoundError(response.content) + raise error + return True