Add GET /api/v4/orbit/graph_status endpoint

What does this MR do and why?

Adds GET /api/v4/orbit/graph_status so consumers can check indexing progress and entity counts for a given namespace or project. The UI will use this to show Knowledge Graph readiness.

  • Accepts one of namespace_id, project_id, or full_path to identify the target
  • Resolves the target to its organization-prefixed traversal_path, then delegates to the GKG gRPC GetGraphStatus RPC
  • Returns project counts, per-domain entity counts (ci, code_review, core, plan, security, source_code), and indexing state
  • Requires knowledge_graph feature flag, authentication, read_knowledge_graph permission, and at least one enabled namespace
  • Returns 404 for user namespaces and personal projects (Knowledge Graph only supports group-backed namespaces)

Why traversal_path instead of querying by ID directly?

The GKG ClickHouse schema partitions all entity tables by traversal_path using startsWith prefix matching. Passing the path lets the service scope queries to exactly the right partition without an extra ID-to-path lookup on the GKG side.

References

ADR 10 and relates to gitlab-org/orbit/knowledge-graph#175

How to set up and validate locally

  1. Enable the feature flag and ensure the user has reporter+ access on a group:

    Feature.enable(:knowledge_graph)
  2. Create a PAT with api scope, then hit the endpoint with any of the three lookup strategies:

    # By group
    curl -H "PRIVATE-TOKEN: <token>" "http://127.0.0.1:3000/api/v4/orbit/graph_status?namespace_id=<GROUP_ID>"
    
    # By project
    curl -H "PRIVATE-TOKEN: <token>" "http://127.0.0.1:3000/api/v4/orbit/graph_status?project_id=<PROJECT_ID>"
    
    # By full path
    curl -H "PRIVATE-TOKEN: <token>" "http://127.0.0.1:3000/api/v4/orbit/graph_status?full_path=<GROUP_OR_PROJECT_PATH>"

    Group scope returns counts across all child projects; project scope narrows to that single project.

  3. Verify the response shape and error cases per the table below.

Error cases

Scenario Expected
No auth token 401
knowledge_graph flag disabled 404
No lookup param / multiple lookup params 400
Non-existent ID or path 404
Private group/project user cannot access 404
User namespace or personal project 404
User has no enabled namespaces 403
GKG gRPC service unreachable 503
Example 200 response (group scope)
{
    "projects": {
        "indexed": 1,
        "total_known": 3
    },
    "domains": [
        {
            "name": "ci",
            "items": [
                {"name": "Deployment", "count": 3},
                {"name": "Environment", "count": 2},
                {"name": "Job", "count": 6},
                {"name": "Pipeline", "count": 4},
                {"name": "Stage", "count": 4}
            ]
        },
        {
            "name": "code_review",
            "items": [
                {"name": "MergeRequest", "count": 5},
                {"name": "MergeRequestDiff", "count": 3},
                {"name": "MergeRequestDiffFile", "count": 5}
            ]
        },
        {
            "name": "core",
            "items": [
                {"name": "Group", "count": 1},
                {"name": "Note", "count": 5},
                {"name": "Project", "count": 3},
                {"name": "User", "count": 0}
            ]
        },
        {
            "name": "plan",
            "items": [
                {"name": "Label", "count": 3},
                {"name": "Milestone", "count": 2},
                {"name": "WorkItem", "count": 4}
            ]
        },
        {
            "name": "security",
            "items": [
                {"name": "Finding", "count": 2},
                {"name": "SecurityScan", "count": 2},
                {"name": "Vulnerability", "count": 3},
                {"name": "VulnerabilityIdentifier", "count": 2},
                {"name": "VulnerabilityOccurrence", "count": 2},
                {"name": "VulnerabilityScanner", "count": 2}
            ]
        },
        {
            "name": "source_code",
            "items": [
                {"name": "Branch", "count": 3},
                {"name": "Definition", "count": 3},
                {"name": "Directory", "count": 2},
                {"name": "File", "count": 5},
                {"name": "ImportedSymbol", "count": 2}
            ]
        }
    ],
    "indexing": {
        "state": "indexing",
        "last_started_at": "2026-04-26T21:10:00+00:00",
        "last_completed_at": "2026-04-26T20:50:00+00:00",
        "last_duration_ms": 45000,
        "last_error": null
    }
}

Tested against a local GDK with the GKG service running — all three lookup strategies return the expected response shape with entity counts and indexing state from ClickHouse.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Jean-Gabriel Doyon

Merge request reports

Loading