imbajin commented on code in PR #3402:
URL: https://github.com/apache/hugegraph/pull/3402#discussion_r4366075242


##########
helm/hugegraph/examples/seatunnel/clone/copy-schema.py:
##########
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# 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.
+
+"""Copy schema to an existing empty graph, preserving IDs for subsequent data 
copy."""
+import argparse
+import base64
+import copy
+import json
+import os
+import urllib.request
+
+KINDS = ('propertykeys', 'vertexlabels', 'edgelabels', 'indexlabels')
+
+
+def client(base, role):
+    user = os.environ.get('HG_' + role + '_USERNAME', 'admin')
+    password = os.environ['HG_' + role + '_PASSWORD']
+    token = base64.b64encode((user + ':' + password).encode()).decode()
+
+    def request(method, suffix, body=None):
+        data = None if body is None else json.dumps(body).encode()
+        req = urllib.request.Request(base.rstrip('/') + suffix, data=data,
+                                     method=method, headers={
+                                         'Authorization': 'Basic ' + token,
+                                         'Content-Type': 'application/json'})
+        with urllib.request.urlopen(req, timeout=60) as response:
+            raw = response.read().decode()
+            return json.loads(raw) if raw else None
+    return request
+
+
+def normalize(schema):
+    result = copy.deepcopy(schema)
+    for kind in KINDS:
+        for element in result[kind]:
+            element.pop('status', None)
+            element.get('user_data', {}).pop('~create_time', None)
+            for key in ('properties', 'nullable_keys', 'index_labels'):
+                if key in element:
+                    element[key].sort()
+        result[kind].sort(key=lambda element: element['name'])
+    return result
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('--source', required=True, help='Source graph URL, 
including graph name')
+    parser.add_argument('--target', required=True, help='Target graph URL, 
including graph name')
+    args = parser.parse_args()
+    source = client(args.source, 'SOURCE')
+    target = client(args.target, 'TARGET')
+    schema = source('GET', '/schema')
+    target_schema = target('GET', '/schema')
+    if any(target_schema[kind] for kind in KINDS):
+        raise SystemExit('Target schema must be empty; use a new graph.')
+    original_mode = target('GET', '/mode')['mode']
+    if str(original_mode).upper() != 'NONE':
+        raise SystemExit('Target graph must be in NONE mode before copying 
schema.')
+    target('PUT', '/mode', 'RESTORING')
+    try:
+        for kind in KINDS:
+            for element in schema[kind]:
+                body = copy.deepcopy(element)
+                body.pop('status', None)
+                body.pop('index_labels', None)
+                if kind == 'indexlabels':
+                    body['rebuild'] = False  # Empty graph; subsequent data 
writes build indexes.
+                target('POST', '/schema/' + kind, body)

Review Comment:
   ⚠️ Important: Recreate inherited edge labels in dependency order and remove 
derived parent links. For a valid graph with a parent and at least one 
sublabel, `GraphSONSchemaSerializer.writeEdgeLabel()` exports the parent with 
nonempty `links`. This loop posts those links unchanged, while 
`EdgeLabelAPI.convert2Builder()` forwards them to 
`EdgeLabelBuilder.checkRelation()`, which rejects parent labels unless their 
links are empty. Copying therefore fails after partially creating the target 
schema, and the empty-target guard prevents retrying it. The response order 
also does not guarantee that a parent exists before `withBase()` creates its 
child. Please create parents with their derived links removed, create dependent 
sublabels afterward so their registration reconstructs those links, and add a 
parent/sub schema roundtrip regression.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to