Skip to content

feat(server): adapt Hubble 2.0 frontend APIs and implement default graph/role management#3008

Draft
Yeaury wants to merge 7 commits intoapache:masterfrom
Yeaury:feat/hubble-api-compatibility
Draft

feat(server): adapt Hubble 2.0 frontend APIs and implement default graph/role management#3008
Yeaury wants to merge 7 commits intoapache:masterfrom
Yeaury:feat/hubble-api-compatibility

Conversation

@Yeaury
Copy link
Copy Markdown

@Yeaury Yeaury commented Apr 24, 2026

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 default
  • GET /graphspaces/{graphspace}/graphs/{name}/undefault — Unset a graph as default
  • GET /graphspaces/{graphspace}/graphs/default — Get current user's default graph list
  • PUT /graphspaces/{graphspace}/graphs/{name} — Graph management operations (currently supports update action for nickname updates)
  • POST /graphspaces/{graphspace}/graphs (form-urlencoded) — Hubble frontend form-based graph creation compatibility
  • Auto-fill HStore/PD mode defaults (backend=hstore, serializer=binary, store={name}) during graph creation, and map frontend schema field to schema.init_template

2. 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 role
  • DELETE /graphspaces/{graphspace}/role — Delete default role
  • Remove unnecessary PD mode restriction from listProfile endpoint
  • Add @JsonIgnoreProperties(ignoreUnknown = true) to tolerate unknown fields from the frontend

3. ManagerAPI — Role Query (ManagerAPI.java)

  • GET /auth/manager/default — New endpoint for Hubble frontend to query if the current user has a specified default role

4. SchemaTemplateAPI — Schema Template CRUD (New File)

  • GET /graphspaces/{graphspace}/schematemplates — List all schema templates
  • GET /graphspaces/{graphspace}/schematemplates/{name} — Get a specific template
  • POST /graphspaces/{graphspace}/schematemplates — Create template
  • PUT /graphspaces/{graphspace}/schematemplates/{name} — Update template
  • DELETE /graphspaces/{graphspace}/schematemplates/{name} — Delete template

5. Authentication & Authorization Layer (AuthManager.java, StandardAuthManager.java, StandardAuthManagerV2.java, HugeGraphAuthProxy.java)

  • Added 10 new methods to AuthManager interface: setDefaultGraph/unsetDefaultGraph/getDefaultGraph and createDefaultRole/createSpaceDefaultRole/isDefaultRole/deleteDefaultRole, etc.
  • StandardAuthManager and StandardAuthManagerV2 implement the above interfaces using existing HugeGroup/HugeBelong/HugeRole metadata mechanisms
  • HugeGraphAuthProxy.AuthManagerProxy adds corresponding delegate methods to properly forward calls to the underlying authManager

6. 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 methods

Verifying these changes

  • Need tests and can be verified as follows:
    • Verify the full graph management flow via Hubble frontend (create graph, list, profile query)
    • Verify default graph set/unset/query via Hubble frontend
    • Verify Schema template CRUD operations
    • Verify default role (SPACE/ANALYST/OBSERVER) creation, query, and deletion
    • Directly test all new endpoints via REST API

Does this PR potentially affect the following parts?

  • Dependencies (add/update license info & regenerate_known_dependencies.sh)
  • Modify configurations
  • The public API
  • Other affects (typed here)
  • Nope

Documentation Status

  • Doc - TODO
  • Doc - Done
  • Doc - No Need

Yeaury added 5 commits April 24, 2026 10:53
- 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()
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 AuthManager interface and its implementations/proxies.
  • Introduced schema template CRUD API and corresponding GraphManager helpers.

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

  • configs is only validated as non-null when clone_graph_name is empty. If clone_graph_name is provided and the request body is omitted/empty, configs can be null and convConfig(configs) will throw a NullPointerException in the clone branch. Consider defaulting configs to an empty map (or making convConfig() 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.

Comment on lines +165 to +168
HugeConfig config = (HugeConfig) hg.configuration();
String configResp = ConfigUtil.writeConfigToString(config);
Map<String, Object> profile =
JsonUtil.fromJson(configResp, Map.class);
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copilot uses AI. Check for mistakes.
Comment on lines +134 to +138
@PUT
@Timed
@Path("{name}")
@Produces(APPLICATION_JSON_WITH_CHARSET)
public String update(@Context GraphManager manager,
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@QueryParam("role") String role,
@QueryParam("graph") String graph) {
LOG.debug("check if current user is default role: {} {} {}",
role, graphSpace, graph);
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Suggested change
role, graphSpace, graph);
role, graphSpace, graph);
ensurePdModeEnabled(manager);

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +131
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");
}
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +204 to +207
if (!authManager.isAdminManager(HugeGraphAuthProxy.username()) &&
role.equalsIgnoreCase(HugeDefaultRole.SPACE.toString())) {
throw new HugeException("Forbidden to delete role %s", role);
}
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to 38
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;
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copilot uses AI. Check for mistakes.
…#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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

‼️ Bug: nickname 更新未持久化,且 isExistedGraphNickname 在非 PD 模式下会 NPE

两个问题:

  1. exist.nickname(nickname) 只修改了内存中的 StandardHugeGraph.nickname 字段,没有调用 metaManager.updateGraphConfig() 写回存储。创建图时有完整的持久化链路(GraphManager:1345 设内存 → :1353 写 metaManager),但这里的更新路径缺少持久化步骤,重启后 nickname 会丢失。

  2. 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");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

‼️ Bug: auto-fill backend=hstore 会导致非 PD 模式创建图失败

GraphsAPI 的创建端点在非 PD(RocksDB 单机)模式下也会被调用(通过 graphspace=DEFAULT)。这里无条件填充 backend=hstore 会导致单机版用户创建图时使用错误的后端,应该加 PD 模式判断:

Suggested change
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));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

‼️ Security: 序列化全部配置项,可能泄露敏感信息

config.getKeys() 会遍历所有配置键值对,包括可能包含密码、token 等敏感字段(如 gremlin.server.password、认证相关配置等)。该方法的返回值会通过 GraphsAPI.listProfile() 返回给前端展示。

建议做白名单过滤(只输出前端需要的字段),或者至少排除包含 passwordsecrettokencredential 等关键词的 key。

"Must pass graphspace and role params");

HugeDefaultRole defaultRole =
HugeDefaultRole.valueOf(role.toUpperCase());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

‼️ Bug: HugeDefaultRole.valueOf() 未做异常处理,非法 role 值返回 500

valueOf(role.toUpperCase()) 在 role 值非法时会抛出 IllegalArgumentException,变成 HTTP 500 而不是 400。同一个 PR 中 GraphSpaceAPI.setDefaultRole() 已经用 try-catch 做了处理,这里应该保持一致:

Suggested change
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) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 重复代码: 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 &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 校验过严: actionMap.size() == 2 会在前端多传字段时直接 400

要求请求体恰好包含 2 个 key。如果前端在 JSON 中额外带了字段(很常见的兼容场景),请求会被拒绝。建议放宽为只校验必需字段:

Suggested change
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";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 建议加注释说明 marker group 设计的上下文

使用 ~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");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 建议使用 DateTimeFormatter 替代 SimpleDateFormat

SimpleDateFormat 虽然这里是局部变量(线程安全),但 DateTimeFormatter 是 Java 8+ 推荐的替代方案,线程安全且可复用为 static final 常量,性能更好:

private static final DateTimeFormatter DATE_FORMAT =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

@imbajin
Copy link
Copy Markdown
Member

imbajin commented Apr 25, 2026

新增 API 单机版(非 PD)兼容性对照

目标:社区默认的 RocksDB 单机版通过 graphspace=DEFAULT 使用 GraphsAPI,应支持图的增删改查和基础权限管理。GraphSpaceAPI、ManagerAPI、SchemaTemplateAPI 属于 PD 专属功能,不需要兼容。

需要兼容单机版的端点(GraphsAPI)

API 端点 当前状态 问题说明 需要的改动
GET .../graphs/profile ⚠️ ConfigUtil.writeConfigToString 序列化全部配置项,可能泄露 password/token 等敏感字段 白名单过滤或排除敏感 key
PUT .../graphs/{name} (manage/update nickname) 1. isExistedGraphNickname() 调用 metaManager.graphConfigs() — 非 PD 下 MetaManager 未初始化,NPE;2. exist.nickname() 只改内存,缺少持久化调用 1. 加 isPDEnabled() 分支,非 PD 下用内存图列表做去重;2. 补充持久化调用
POST .../graphs (create JSON) 无条件执行 configs.putIfAbsent("backend", "hstore"),单机版默认 RocksDB 会被覆盖为 hstore 导致创建失败 if (manager.isPDEnabled()) 包裹 hstore/binary 默认值填充
POST .../graphs/{name} (createByText) 委托 create() 方法,同上 同上(修复 create 即可)
POST .../graphs/{name}/default (设置默认图) StandardAuthManager 已实现
DELETE .../graphs/{name}/default (取消默认图) StandardAuthManager 已实现
GET .../graphs/default (查询默认图) StandardAuthManager 已实现

PD 专属端点(无需兼容单机版)

API 端点 归属 说明
POST/GET/DELETE .../graphspaces/{gs}/role GraphSpaceAPI 图空间级角色管理,PD-only by design
GET /auth/manager/default ManagerAPI 已有 ensurePdModeEnabled() 门禁
GET/POST/PUT/DELETE .../schematemplates[/{name}] SchemaTemplateAPI 全部走 metaManager,PD-only

总结:7 个 GraphsAPI 端点需要兼容单机版。其中 3 个需要代码改动(manage 需加 isPDEnabled() 分支 + 持久化修复,create/createByText 需条件化 hstore 默认值),1 个需过滤敏感配置,3 个已可正常工作。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants