[Feature] Unify REST API response format for server, pd and store modules #2975#3029
[Feature] Unify REST API response format for server, pd and store modules #2975#3029BrandaoFelipe wants to merge 6 commits into
Conversation
|
I think the current implementation does not actually establish one unified runtime path yet. It adds new mappers/advice, but several existing paths either bypass them or now compete with them. Current shape: The main design issues are:
That makes the effective behavior unclear. If the new
I suggest first defining the response contract and then updating the existing runtime paths instead of adding parallel ones: One unrelated item: |
|
A larger concern is that this PR changes the public REST API response contract. The server / pd / store REST APIs may already be consumed by external services, so a large response-format change can have significant downstream impact. Current response shapes include fields such as: The new envelope introduces a different shape: That can break clients that parse existing fields like I think we should first decide the compatibility and migration strategy, for example: If this is intended to be a breaking change, it should probably go through community discussion and come with docs, migration notes, OpenAPI updates, and REST-level compatibility tests. If it is not intended to be breaking, then the implementation needs an explicit compatibility layer rather than replacing existing response bodies directly. In short: |
|
Hi @imbajin, Server: integrate the unified formatting into the existing ExceptionFilter infrastructure.
Do you think it would be better to continue this discussion in the PR thread, or would this be more appropriate for a broader dev mailing list discussion first? EDIT: |
|
IMO, a controller should only be responsible for receiving external requests and returning responses — exception handling logic does not belong there. We should remove the catch blocks from controllers and let a centralized handler take care of error formatting. For the store business code, rather than replacing it with the HTTP status code, we could simply add a dedicated field (e.g. That said, it's worth classifying controllers by their audience — user-facing, admin-facing, or internal module-facing. Internal module-facing interfaces may reasonably keep their existing response format unchanged, while the unified response format should at minimum be enforced across all user-facing endpoints. On compatibility: could we treat this as a breaking change and land it in the next major/minor version with proper release notes and migration guidance? |
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared ApiResponse envelope and adds centralized REST exception handling across server (Jersey), PD, and store-node (Spring Boot) modules to make error responses more consistent and avoid leaking raw internal failures.
Changes:
- Adds
ApiResponse<T>inhugegraph-common. - Adds module-specific and generic exception handlers for server, PD, and store-node REST APIs.
- Adds PD mapper tests and changes PD service Spring Boot repackage output classifier.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/rest/response/ApiResponse.java |
Adds the shared response envelope type. |
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/HugeExceptionMapper.java |
Adds Jersey handling for HugeException. |
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/GenericExceptionMapper.java |
Adds Jersey fallback handling for generic exceptions. |
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java |
Adds Spring handling for PDException. |
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/GenericExceptionMapper.java |
Adds Spring fallback handling for PD service exceptions. |
hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/rest/exceptions/PDExceptionMapperTest.java |
Adds unit coverage for PD exception mappers. |
hugegraph-pd/hg-pd-service/pom.xml |
Changes Spring Boot repackage artifact classifier. |
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java |
Adds Spring handling for HgStoreException. |
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/GenericExceptionMapper.java |
Adds Spring fallback handling for store-node exceptions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private HttpStatus resolveStatus(int code) { | ||
| try { | ||
| // Tenta mapear códigos HTTP exatos (ex: 400, 404, 500) | ||
| return HttpStatus.valueOf(code); | ||
| } catch (IllegalArgumentException e) { | ||
| // Se falhar (ex: 4001), extraímos o primeiro dígito para descobrir a família do erro | ||
| String codeStr = String.valueOf(code); | ||
|
|
||
| if (codeStr.startsWith("4")) { | ||
| return HttpStatus.BAD_REQUEST; // Erros de cliente -> 400 | ||
| } | ||
|
|
||
| // Tudo que for da família 5000 ou não reconhecido vira Erro de Servidor -> 500 | ||
| return HttpStatus.INTERNAL_SERVER_ERROR; | ||
| } | ||
| } |
| @Provider | ||
| public class HugeExceptionMapper implements ExceptionMapper<HugeException> { |
| <mainClass> | ||
| org.apache.hugegraph.pd.boot.HugePDServer | ||
| </mainClass> | ||
| <classifier>exec</classifier> |
VGalaxies
left a comment
There was a problem hiding this comment.
Review summary
- Blocking: yes
- Summary: The PR can break PD distribution startup and introduces inconsistent error mappings in the new unified response handlers.
- Evidence:
git diff origin/master...HEADmvn -pl hugegraph-commons/hugegraph-common,hugegraph-server/hugegraph-api,hugegraph-pd/hg-pd-service,hugegraph-store/hg-store-node -am -DskipTests -Dmaven.javadoc.skip=true compilepassed
| <mainClass> | ||
| org.apache.hugegraph.pd.boot.HugePDServer | ||
| </mainClass> | ||
| <classifier>exec</classifier> |
There was a problem hiding this comment.
High: PD executable jar is no longer the artifact used by the distribution
hugegraph-pd/hg-pd-service/pom.xml:178
Evidence
- The new
<classifier>exec</classifier>makes Spring Boot attach the repackaged executable jar as a classified artifact, whilehugegraph-pd/hg-pd-dist/src/assembly/descriptor/server-assembly.xml:52includes the unclassifiedhg-pd-servicedependency andstart-hugegraph-pd.sh:172-176runs${LIB}/hg-pd-service-*.jarwithjava -jar.
Impact
- The PD dist will package/run the plain unclassified jar instead of the executable Spring Boot jar, so packaged PD startup can fail or run the wrong artifact.
Requested fix
- Either remove the classifier so the main artifact remains executable, or update
hg-pd-distto depend on and include theexecclassifier explicitly and make the startup glob select only that jar.
| } | ||
|
|
||
| // Tudo que for da família 5000 ou não reconhecido vira Erro de Servidor -> 500 | ||
| return HttpStatus.INTERNAL_SERVER_ERROR; |
There was a problem hiding this comment.
Medium: Real PD error codes are mapped to HTTP 500
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java:67
Evidence
resolveStatus()only treats exact HTTP codes or codes starting with4as client errors; actual PD codes includeSTORE_ID_NOT_EXIST = 101,NOT_FOUND = 103, andPD_UNREACHABLE = 104inhugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto:134-139, so those become500.
Impact
- Uncaught domain errors such as not-found/store-not-found are reported as server failures, which breaks HTTP semantics and can mislead clients, retries, and monitoring.
Requested fix
- Map the existing
Pdpb.ErrorTypevalues explicitly to appropriate HTTP statuses, or preserve the previous REST status behavior while only changing the response body format.
| } | ||
|
|
||
| ApiResponse<Object> apiResponse = new ApiResponse<>( | ||
| status.value(), |
There was a problem hiding this comment.
Medium: Store response body drops the HgStoreException code
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java:57
Evidence
- The handler reads
exception.getCode()at line 37, but constructsApiResponsewithstatus.value()at line 57. Store exceptions define machine-readable codes such as1001and1201inHgStoreException.java:23-40.
Impact
- Clients receive only HTTP codes like
400or500in the unifiedcodefield and lose the store-specific error code needed to distinguish validation, closed-store, RocksDB, PD, and metric failures.
Requested fix
- Use
errorCodeforApiResponse.codeand keep the HTTP status only in theResponseEntitystatus.
Purpose of the PR
The purpose of this PR is to standardize and unify the REST API global error response format across the core modules (
pd,serverandstore/hg-store-node) using the centralizedApiResponseenvelope wrapper.Currently, unhandled exceptions or module-specific errors could return inconsistent formats or expose raw stack traces to the client. This tracking change enforces a clean, predictable JSON error contract across different frameworks used in the ecosystem (Jersey and Spring Boot), enhancing API security and client-side error handling.
Main Changes
This PR introduces centralized exception mapping logic across two distinct sub-framework layers within the project:
1.
hugegraph-servermodule (Jersey / JAX-RS)HugeExceptionMapper: Intercepts allHugeExceptionoccurrences. It safely resolves the underlying root cause using a recursiverootCause()lookup and maps internal core errors directly to appropriate HTTP Status Codes (e.g.,IllegalArgumentException-> 400 Bad Request,NoSuchElementException-> 404 Not Found) wrapped cleanly insideApiResponse.GenericExceptionMapper: Implemented as a top-level global catch-all provider forException.classto prevent infrastructure leakages, mapping unexpected exceptions to a secure HTTP 500"An unexpected error occurred"message payload.2.
hugegraph-storemodule (hg-store-nodesub-module / Spring Boot)StoreExceptionHandler: Created using Spring's@RestControllerAdviceto interceptHgStoreException. It dynamically parses the exception's internal errorcodeto differentiate client errors from infrastructure faults based on explicit ranges:[1200, 1300)(e.g., RocksDB storage failures) -> 500 Internal Server Error (logged asSEVERE/ERRORwith full stack trace).[1000, 1200)(e.g., Unsupported data formats) -> 400 Bad Request (logged cleanly as aWARNINGmessage).GenericExceptionMapper: Added as a fallback interceptor for standardException.classinstances inside the store node rest controllers to ensure uniform 500 error envelopes using the Log4j wrapper.3.
hugegraph-pdmodule (hg-pd-servicesub-module / Spring Boot)PdExceptionHandler: Implemented via@RestControllerAdviceto handle metadata and coordination cluster exceptions (PDException). Centralizes cluster control plane failure logging (Raft state, partition routing) and converts them into the unifiedApiResponseschema.Exception.classmapping to gracefully mask unhandled runtime infrastructure glitches with a generic"An unexpected error occurred in the cluster control plane"feedback text.Verifying these changes
mvn clean compile -DskipTestsfrom the root directory to confirm proper multi-module compile-time dependency alignment (validatingApiResponsecross-package visibility insidehg-store-node).{ "status": 400, "message": "Specific validation/error message string", "data": null, "statusText": "Bad Request" }Does this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No Need