bitflicker64 commented on code in PR #3241:
URL: https://github.com/apache/hugegraph/pull/3241#discussion_r4113115938


##########
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherErrorMapper.java:
##########
@@ -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.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+/**
+ * Classify cypher execution failures into stable error codes and attach
+ * actionable hints for the most common, cryptic translator messages.
+ */
+public final class CypherErrorMapper {
+
+    public static final String SYNTAX_ERROR = "HugeGraph.Cypher.SyntaxError";
+    public static final String UNSUPPORTED_FEATURE =
+            "HugeGraph.Cypher.UnsupportedFeature";
+    public static final String MISSING_PARAMETER =
+            "HugeGraph.Cypher.MissingParameter";
+    public static final String EXECUTION_ERROR = 
"HugeGraph.Cypher.ExecutionError";
+
+    private CypherErrorMapper() {
+    }
+
+    public static CypherModel.CypherError map(Throwable e) {
+        String message = e.getMessage() != null ? e.getMessage() : 
e.toString();
+        // strip noisy exception class prefixes, e.g. 
"...driver.exception.ResponseException: "
+        message = message.replaceFirst(
+                "^([a-zA-Z0-9_]+\\.)+[A-Za-z0-9_]+(Exception|Error):\\s*", "");
+        String lower = message.toLowerCase();
+
+        if (lower.contains("not defined") && lower.contains("$")) {
+            return new CypherModel.CypherError(MISSING_PARAMETER, message,
+                    "Provide the value via the 'parameters' query argument, " +
+                    "e.g. ?parameters=%7B%22city%22%3A%20%22Beijing%22%7D");
+        }
+        if (lower.contains("undefined vertex label")
+                || lower.contains("undefined edge label")
+                || lower.contains("undefined property key")) {
+            return new CypherModel.CypherError(EXECUTION_ERROR, message,
+                    "Create the schema element via the schema API before " +
+                    "running the query");
+        }
+        if (lower.contains("not defined") || lower.contains("undefined")) {

Review Comment:
   Important: `SyntaxError` is assigned only when the message contains "not 
defined" or "undefined", so real parse errors fall through to `ExecutionError` 
with an empty hint on line 65. The PR's goal is to let clients branch on the 
failure mode, and these codes are described as stable, so a client that checks 
for `HugeGraph.Cypher.SyntaxError` will miss most syntax errors.
   
   Evidence: I ran `CypherAst.parse(q, {})` from translation 1.0.4 with 
`gremlin+cfog_server_extensions+inline_parameters`:
   - `MATCH (n:person RETURN n` gives `SyntaxException: Invalid input 'R': 
expected ...`, mapped to `ExecutionError`.
   - `MATCH (n:person) RETRUN n` gives `Invalid input 'R': expected 'u/U'`, 
mapped to `ExecutionError`.
   - `MATCH (n) RETURN n; MATCH (m) RETURN m` gives `Expected exactly one 
statement per query but got: 2`, mapped to `ExecutionError`.
   - `RETURN date()` gives `SyntaxException: Unknown function 'date'`, mapped 
to `ExecutionError`.
   - `MATCH (n:person) RETURN not_defined_var` gives "Variable 
`not_defined_var` not defined", the only one mapped to `SyntaxError`.
   
   The PR description's own live check shows the same result: a syntax error 
returned `HugeGraph.Cypher.ExecutionError` with `"hint":""`. The branch also 
catches any other HugeGraph message with "undefined", such as `Undefined index 
label: 'x'`, and tells the user to declare a variable.
   
   Requested change: match the parser messages explicitly (`invalid input`, 
`expected exactly one statement`, `unknown function`, `variable ... not 
defined`) for `SyntaxError`, narrow the variable hint to the `Variable ... not 
defined` message, and add a test that sends a real parse error such as `MATCH 
(n:person RETURN n` and asserts `"code":"HugeGraph.Cypher.SyntaxError"`.



##########
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherErrorMapper.java:
##########
@@ -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.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+/**
+ * Classify cypher execution failures into stable error codes and attach
+ * actionable hints for the most common, cryptic translator messages.
+ */
+public final class CypherErrorMapper {
+
+    public static final String SYNTAX_ERROR = "HugeGraph.Cypher.SyntaxError";
+    public static final String UNSUPPORTED_FEATURE =
+            "HugeGraph.Cypher.UnsupportedFeature";
+    public static final String MISSING_PARAMETER =
+            "HugeGraph.Cypher.MissingParameter";
+    public static final String EXECUTION_ERROR = 
"HugeGraph.Cypher.ExecutionError";
+
+    private CypherErrorMapper() {
+    }
+
+    public static CypherModel.CypherError map(Throwable e) {
+        String message = e.getMessage() != null ? e.getMessage() : 
e.toString();
+        // strip noisy exception class prefixes, e.g. 
"...driver.exception.ResponseException: "
+        message = message.replaceFirst(
+                "^([a-zA-Z0-9_]+\\.)+[A-Za-z0-9_]+(Exception|Error):\\s*", "");
+        String lower = message.toLowerCase();
+
+        if (lower.contains("not defined") && lower.contains("$")) {

Review Comment:
   Minor: This branch cannot fire with the server's default configuration, so 
`MissingParameter` is a published code that is never returned. 
`CypherClient.createRequest()` sends no bindings, and `CypherOpProcessor` uses 
`inline_parameters`. With an empty parameter map, translation 1.0.4 turns 
`MATCH (n:person) WHERE n.name = $name RETURN n` into a traversal that compares 
against `'  cypher.null'` and raises no error, so the query returns no rows. 
Variable-not-defined messages never contain `$`.
   
   Requested change: drop `MISSING_PARAMETER` and this branch until the API 
accepts parameters, together with the `?parameters=` hint already raised on 
line 46.



##########
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java:
##########
@@ -37,6 +37,9 @@ public class CypherModel {
     @Schema(description = "The query result")
     public Result result = new Result();
 
+    @Schema(description = "Structured errors, empty on success")
+    public List<CypherError> errors = Collections.emptyList();

Review Comment:
   Critical: This field breaks every existing Java client call to `/cypher`, 
successful queries included. `errors` defaults to `Collections.emptyList()` and 
Jersey's `JacksonFeature` serializes it, so every response now ends with 
`"errors":[]`. hugegraph-toolchain's `CypherAPI.post` parses the body with 
`RestResult.readObject(org.apache.hugegraph.structure.gremlin.Response.class)`. 
That class declares only `requestId`, `status` and `result` and has no 
`ignoreUnknown`, and `RestResult` uses a plain `ObjectMapper`, where 
`FAIL_ON_UNKNOWN_PROPERTIES` is on by default.
   
   Evidence: with `hugegraph-client-1.8.0.jar` and 
`hugegraph-common-1.7.0.jar`, `new RestResult(200, body, 
null).readObject(Response.class)` parses the current response shape. With 
`,"errors":[]` appended it throws `SerializeException` caused by 
`UnrecognizedPropertyException: Unrecognized field "errors" (class 
org.apache.hugegraph.structure.gremlin.Response), not marked as ignorable`. 
`CypherManager.execute()` and Hubble's `QueryService.executeCypher()` both go 
through that path. CI does not catch this because `CypherApiTest` checks the 
body as a string.
   
   Requested change: keep the top-level shape unchanged. The existing 
`status.attributes` map (`Map<String, Object>`, already accepted by the client 
as `Map<String, ?>`) can carry the code and hint on failures, for example 
`attributes = {"code": ..., "hint": ...}`. If a top-level `errors` field is 
still wanted, land `@JsonIgnoreProperties(ignoreUnknown = true)` on the 
client's `Response` in hugegraph-toolchain and release it first, and do not 
emit the field on success in the meantime.



-- 
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