feat(server): adapt Hubble 2.0 frontend APIs and implement default graph/role management#3008
feat(server): adapt Hubble 2.0 frontend APIs and implement default graph/role management#3008Yeaury wants to merge 7 commits intoapache:masterfrom
Conversation
- Add listProfile endpoint with default graph sorting and prefix filtering - Add setDefault/unsetDefault/getDefault endpoints for default graph management - Add manage(PUT) endpoint for graph nickname update - Add createByForm for form-urlencoded graph creation compatibility - Auto-fill HStore/PD defaults (backend/serializer/store) during graph creation
- Add setDefaultRole/checkDefaultRole/deleteDefaultRole in GraphSpaceAPI - Add checkDefaultRole endpoint in ManagerAPI - Add default role interfaces in AuthManager - Implement default role CRUD in StandardAuthManager and StandardAuthManagerV2 - Add stub proxy methods in HugeGraphAuthProxy
- Add new SchemaTemplateAPI with list/get/create/update/delete operations - Fix package path from api.profile to api.space - Use HugeGraphAuthProxy.username() instead of authManager.username()
There was a problem hiding this comment.
Pull request overview
This PR adds/extends HugeGraph Server REST endpoints and auth-layer capabilities needed by the Hubble 2.0 frontend, focusing on graph profile listing, default graph selection, default role management, and schema template CRUD within graphspaces.
Changes:
- Added graph profile listing + default-graph set/unset/query APIs, plus graph create compatibility tweaks.
- Implemented default-graph/default-role persistence methods in the
AuthManagerinterface and its implementations/proxies. - Introduced schema template CRUD API and corresponding
GraphManagerhelpers.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/util/ConfigUtil.java | Adds config-to-string helper used by graph profile listing. |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java | Extends auth interface for default graph/role operations. |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java | Implements new default graph/role methods (non-PD auth manager). |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManagerV2.java | Implements new default graph/role methods (PD-mode auth manager). |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java | Proxies/delegates new AuthManager methods. |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java | Adds schema template management helpers. |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java | Adds graph profile listing, default graph APIs, manage/update behavior, and create defaults. |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/space/GraphSpaceAPI.java | Adds default role management endpoints and JSON tolerance. |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/ManagerAPI.java | Adds endpoint to query whether current user has a default role. |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/space/SchemaTemplateAPI.java | New schema template CRUD endpoint implementation. |
Comments suppressed due to low confidence (1)
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java:443
configsis only validated as non-null whenclone_graph_nameis empty. Ifclone_graph_nameis provided and the request body is omitted/empty,configscan be null andconvConfig(configs)will throw aNullPointerExceptionin the clone branch. Consider defaultingconfigsto an empty map (or makingconvConfig()null-safe) before using it for cloning.
// Check required parameters for creating graph
if (StringUtils.isEmpty(clone)) {
// Only check required parameters when creating new graph, not when cloning
E.checkArgument(configs != null, "Config parameters cannot be null");
// Auto-fill defaults for PD/HStore mode when not provided
configs.putIfAbsent("backend", "hstore");
configs.putIfAbsent("serializer", "binary");
configs.putIfAbsent("store", name);
// Map frontend 'schema' field to backend config key
Object schema = configs.remove("schema");
if (schema != null && !schema.toString().isEmpty()) {
configs.put("schema.init_template", schema.toString());
}
}
String creator = HugeGraphAuthProxy.username();
if (StringUtils.isNotEmpty(clone)) {
// Clone from existing graph
LOG.debug("Clone graph '{}' to '{}' in graph space '{}'", clone, name, graphSpace);
graph = manager.cloneGraph(graphSpace, clone, name, convConfig(configs));
} else {
// Create new graph
graph = manager.createGraph(graphSpace, name, creator,
convConfig(configs), true);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| HugeConfig config = (HugeConfig) hg.configuration(); | ||
| String configResp = ConfigUtil.writeConfigToString(config); | ||
| Map<String, Object> profile = | ||
| JsonUtil.fromJson(configResp, Map.class); |
There was a problem hiding this comment.
listProfile() parses ConfigUtil.writeConfigToString(config) with JsonUtil.fromJson(...). If the graph is using a local config file, that helper currently returns the raw .properties file content (not JSON), which will cause JSON parsing failures and break this endpoint. Ensure the config is returned in a JSON-compatible format (or avoid JSON parsing and build the profile map directly from HugeConfig).
| @PUT | ||
| @Timed | ||
| @Path("{name}") | ||
| @Produces(APPLICATION_JSON_WITH_CHARSET) | ||
| public String update(@Context GraphManager manager, |
There was a problem hiding this comment.
update() lacks @Consumes(APPLICATION_JSON) and doesn’t call jsonSchemaTemplate.checkUpdate(). If schema is missing/empty, the SchemaTemplate constructor will throw and likely produce a non-actionable error for clients; please add explicit validation and a consistent 400 response.
| @QueryParam("role") String role, | ||
| @QueryParam("graph") String graph) { | ||
| LOG.debug("check if current user is default role: {} {} {}", | ||
| role, graphSpace, graph); |
There was a problem hiding this comment.
checkDefaultRole() doesn’t call ensurePdModeEnabled(manager) while the other endpoints in ManagerAPI do. If this API requires PD mode, add the same guard here to avoid inconsistent behavior when PD is disabled (or explicitly handle the non-PD case).
| role, graphSpace, graph); | |
| role, graphSpace, graph); | |
| ensurePdModeEnabled(manager); |
| String username = HugeGraphAuthProxy.username(); | ||
| boolean isSpace = manager.authManager() | ||
| .isSpaceManager(graphSpace, username); | ||
| if (st.creator().equals(username) || isSpace) { | ||
| manager.dropSchemaTemplate(graphSpace, name); | ||
| } else { | ||
| throw new HugeException("No permission to delete schema template"); | ||
| } |
There was a problem hiding this comment.
Permission failures are thrown as HugeException, which the server maps to HTTP 400 (see ExceptionFilter.HugeExceptionMapper). For authorization failures, please throw jakarta.ws.rs.ForbiddenException (or another WebApplicationException) so clients receive a proper 403 response.
| if (!authManager.isAdminManager(HugeGraphAuthProxy.username()) && | ||
| role.equalsIgnoreCase(HugeDefaultRole.SPACE.toString())) { | ||
| throw new HugeException("Forbidden to delete role %s", role); | ||
| } |
There was a problem hiding this comment.
deleteDefaultRole() throws HugeException for permission denials (non-admin deleting SPACE role), which will be returned as HTTP 400. Please use jakarta.ws.rs.ForbiddenException (403) for authorization failures to match client expectations.
| import org.apache.hugegraph.auth.HugeDefaultRole; | ||
| import org.apache.hugegraph.auth.HugeGraphAuthProxy; | ||
| import org.apache.hugegraph.core.GraphManager; | ||
| import org.apache.hugegraph.define.Checkable; | ||
| import org.apache.hugegraph.exception.HugeException; | ||
| import org.apache.hugegraph.exception.NotFoundException; |
There was a problem hiding this comment.
org.apache.hugegraph.exception.HugeException doesn’t exist in the codebase (exceptions in this package extend org.apache.hugegraph.HugeException). This import will fail compilation; please switch to org.apache.hugegraph.HugeException (or a more specific exception type like jakarta.ws.rs.ForbiddenException where appropriate).
…#3008) - fix: use @POST/@delete for setDefault/unsetDefault (REST semantics) - fix: add null/empty validation before role field access in GraphSpaceAPI to prevent NPE in setDefaultRole/checkDefaultRole/deleteDefaultRole - fix: change isPrefix to private static and guard nickname null in GraphSpaceAPI and GraphsAPI - fix: ConfigUtil.writeConfigToString always returns JSON regardless of whether config was loaded from file, fixing listProfile endpoint - fix: add @RolesAllowed annotations to SchemaTemplateAPI endpoints - fix: use ForbiddenException (403) instead of HugeException (400) for authorization failures in SchemaTemplateAPI and GraphSpaceAPI - fix: correct LOG placeholder count in SchemaTemplateAPI.delete - fix: use HugeException ('%s') format instead of SLF4J '{}' format - fix: replace com.alipay StringUtils with commons-lang3 in ManagerAPI - fix: add @consumes and checkUpdate() validation to SchemaTemplate.update - fix: add ensurePdModeEnabled guard to ManagerAPI.checkDefaultRole - fix: guard configs null access in GraphsAPI.create clone branch
| nickname.equals(exist.nickname()), | ||
| "Nickname '%s' has already existed in graphspace '%s'", | ||
| nickname, graphSpace); | ||
| exist.nickname(nickname); |
There was a problem hiding this comment.
isExistedGraphNickname 在非 PD 模式下会 NPE
两个问题:
-
exist.nickname(nickname)只修改了内存中的StandardHugeGraph.nickname字段,没有调用metaManager.updateGraphConfig()写回存储。创建图时有完整的持久化链路(GraphManager:1345设内存 →:1353写 metaManager),但这里的更新路径缺少持久化步骤,重启后 nickname 会丢失。 -
manager.isExistedGraphNickname()内部调用metaManager.graphConfigs(graphSpace)— 在非 PD(RocksDB 单机)模式下 MetaManager 未初始化,会抛 NPE。GraphsAPI 的端点在非 PD 模式下通过graphspace=DEFAULT正常使用,所以这里需要加isPDEnabled()分支做兼容(可参考GraphManager.graphs()的处理方式)。
| E.checkArgument(value instanceof String && !StringUtils.isEmpty((String) value), | ||
| "Required parameter '%s' is missing or empty", key); | ||
| // Auto-fill defaults for PD/HStore mode when not provided | ||
| configs.putIfAbsent("backend", "hstore"); |
There was a problem hiding this comment.
backend=hstore 会导致非 PD 模式创建图失败
GraphsAPI 的创建端点在非 PD(RocksDB 单机)模式下也会被调用(通过 graphspace=DEFAULT)。这里无条件填充 backend=hstore 会导致单机版用户创建图时使用错误的后端,应该加 PD 模式判断:
| configs.putIfAbsent("backend", "hstore"); | |
| // Auto-fill defaults for PD/HStore mode when not provided | |
| if (manager.isPDEnabled()) { | |
| configs.putIfAbsent("backend", "hstore"); | |
| configs.putIfAbsent("serializer", "binary"); | |
| } | |
| configs.putIfAbsent("store", name); |
| Iterator<String> iterator = config.getKeys(); | ||
| while (iterator.hasNext()) { | ||
| String key = iterator.next(); | ||
| configMap.put(key, config.getProperty(key)); |
There was a problem hiding this comment.
config.getKeys() 会遍历所有配置键值对,包括可能包含密码、token 等敏感字段(如 gremlin.server.password、认证相关配置等)。该方法的返回值会通过 GraphsAPI.listProfile() 返回给前端展示。
建议做白名单过滤(只输出前端需要的字段),或者至少排除包含 password、secret、token、credential 等关键词的 key。
| "Must pass graphspace and role params"); | ||
|
|
||
| HugeDefaultRole defaultRole = | ||
| HugeDefaultRole.valueOf(role.toUpperCase()); |
There was a problem hiding this comment.
HugeDefaultRole.valueOf() 未做异常处理,非法 role 值返回 500
valueOf(role.toUpperCase()) 在 role 值非法时会抛出 IllegalArgumentException,变成 HTTP 500 而不是 400。同一个 PR 中 GraphSpaceAPI.setDefaultRole() 已经用 try-catch 做了处理,这里应该保持一致:
| HugeDefaultRole.valueOf(role.toUpperCase()); | |
| HugeDefaultRole defaultRole; | |
| try { | |
| defaultRole = HugeDefaultRole.valueOf(role.toUpperCase()); | |
| } catch (IllegalArgumentException e) { | |
| E.checkArgument(false, "Invalid role value '%s'", role); | |
| defaultRole = null; // unreachable | |
| } |
| return defaultProfiles; | ||
| } | ||
|
|
||
| private static boolean isPrefix(Map<String, Object> profile, String prefix) { |
There was a problem hiding this comment.
isPrefix 在 GraphsAPI 和 GraphSpaceAPI 中有两份相同实现
GraphSpaceAPI 中也定义了同样签名和逻辑的 isPrefix 方法。建议提取到公共基类 API 或工具类中,避免后续维护中两处不一致。
| @Parameter(description = "Action map: {'action':'update','update':{...}}") | ||
| Map<String, Object> actionMap) { | ||
| LOG.debug("Manage graph '{}' with action '{}'", name, actionMap); | ||
| E.checkArgument(actionMap != null && actionMap.size() == 2 && |
There was a problem hiding this comment.
actionMap.size() == 2 会在前端多传字段时直接 400
要求请求体恰好包含 2 个 key。如果前端在 JSON 中额外带了字段(很常见的兼容场景),请求会被拒绝。建议放宽为只校验必需字段:
| E.checkArgument(actionMap != null && actionMap.size() == 2 && | |
| E.checkArgument(actionMap != null && | |
| actionMap.containsKey(GRAPH_ACTION), | |
| "Invalid request body '%s'", actionMap); |
| return null; | ||
| } | ||
|
|
||
| private static final String DEFAULT_GRAPH_MARKER = "~default_graph"; |
There was a problem hiding this comment.
使用 ~default_graph: 和 ~default_role: 前缀的特殊 group 来模拟默认图/角色关系是一种基于现有机制的 workaround。当前方案可工作,但有以下已知限制:
- 这些 marker group 会出现在
listGroups结果中 - Belong ID 拼接格式(
userId + "->ug->" + groupId)依赖内部约定
建议在此处加几行注释说明设计背景和已知限制,方便后续维护者理解。未来可考虑引入独立的 default graph/role 存储机制来替代。
| String user = HugeGraphAuthProxy.username(); | ||
| Map<String, Date> defaultGraphs = authManager.getDefaultGraph(graphSpace, user); | ||
|
|
||
| SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
There was a problem hiding this comment.
DateTimeFormatter 替代 SimpleDateFormat
SimpleDateFormat 虽然这里是局部变量(线程安全),但 DateTimeFormatter 是 Java 8+ 推荐的替代方案,线程安全且可复用为 static final 常量,性能更好:
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
新增 API 单机版(非 PD)兼容性对照目标:社区默认的 RocksDB 单机版通过 需要兼容单机版的端点(GraphsAPI)
PD 专属端点(无需兼容单机版)
总结:7 个 GraphsAPI 端点需要兼容单机版。其中 3 个需要代码改动(manage 需加 |
Purpose of the PR
Adapt API endpoints required by the Hubble 2.0 frontend to achieve feature parity with the internal version. The community edition of HugeGraph Server currently lacks several critical APIs that the Hubble frontend depends on (graph profile listing, default graph management, default role management, schema templates, etc.), preventing the Hubble frontend from properly using core graph and role management features. This PR adds the missing APIs to enable full integration between the Hubble 2.0 frontend and the community edition Server.
Link apache/hugegraph-toolchain#632
Main Changes
1. GraphsAPI — Graph Management Endpoint Enhancements (
GraphsAPI.java)GET /graphspaces/{graphspace}/graphs/profile— New graph profile listing endpoint with prefix filtering, default-graph-first sorting, and full config info (nickname, create_time, default status, etc.)GET /graphspaces/{graphspace}/graphs/{name}/default— Set a graph as defaultGET /graphspaces/{graphspace}/graphs/{name}/undefault— Unset a graph as defaultGET /graphspaces/{graphspace}/graphs/default— Get current user's default graph listPUT /graphspaces/{graphspace}/graphs/{name}— Graph management operations (currently supportsupdateaction for nickname updates)POST /graphspaces/{graphspace}/graphs(form-urlencoded) — Hubble frontend form-based graph creation compatibilitybackend=hstore,serializer=binary,store={name}) during graph creation, and map frontendschemafield toschema.init_template2. GraphSpaceAPI — Default Role Management (
GraphSpaceAPI.java)POST /graphspaces/{graphspace}/role— Create default role (supports SPACE/ANALYST/OBSERVER)GET /graphspaces/{graphspace}/role— Check if a user/group has a specified default roleDELETE /graphspaces/{graphspace}/role— Delete default rolelistProfileendpoint@JsonIgnoreProperties(ignoreUnknown = true)to tolerate unknown fields from the frontend3. ManagerAPI — Role Query (
ManagerAPI.java)GET /auth/manager/default— New endpoint for Hubble frontend to query if the current user has a specified default role4. SchemaTemplateAPI — Schema Template CRUD (New File)
GET /graphspaces/{graphspace}/schematemplates— List all schema templatesGET /graphspaces/{graphspace}/schematemplates/{name}— Get a specific templatePOST /graphspaces/{graphspace}/schematemplates— Create templatePUT /graphspaces/{graphspace}/schematemplates/{name}— Update templateDELETE /graphspaces/{graphspace}/schematemplates/{name}— Delete template5. Authentication & Authorization Layer (
AuthManager.java,StandardAuthManager.java,StandardAuthManagerV2.java,HugeGraphAuthProxy.java)AuthManagerinterface:setDefaultGraph/unsetDefaultGraph/getDefaultGraphandcreateDefaultRole/createSpaceDefaultRole/isDefaultRole/deleteDefaultRole, etc.StandardAuthManagerandStandardAuthManagerV2implement the above interfaces using existingHugeGroup/HugeBelong/HugeRolemetadata mechanismsHugeGraphAuthProxy.AuthManagerProxyadds corresponding delegate methods to properly forward calls to the underlying authManager6. Utilities (
ConfigUtil.java,GraphManager.java)ConfigUtil.writeConfigToString()— New utility method to serialize graph configuration to string (used by the listProfile endpoint)GraphManager— Added graph management helper methodsVerifying these changes
Does this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No Need