Skip to content

feat(UI): OpenGraph Edge Filtering: BED-6725#2291

Merged
maffkipp merged 21 commits intomainfrom
BED-6725
Jan 28, 2026
Merged

feat(UI): OpenGraph Edge Filtering: BED-6725#2291
maffkipp merged 21 commits intomainfrom
BED-6725

Conversation

@maffkipp
Copy link
Contributor

@maffkipp maffkipp commented Jan 23, 2026

Description

Adds support to our Edge Filtering Dialog under Pathfinding Search on the Explore page for filtering by OpenGraph edge kinds. This functionality is currently gated behind the pathfinding_search feature flag.

When a user has uploaded valid OpenGraph edge kinds, they will now show up in the dialog under a new category called "OpenGraph". Underneath that heading, there will be additional categories for the different schemas these edges are a part of. To be valid, an edge must:

  • Have the property is_traversable set to true
  • Not be a part of one of our built-in schemas, which are currently ad and az.

Motivation and Context

Resolves BED-6725

How Has This Been Tested?

Additional tests added that cover the new behavior added and that the feature flag is gating behavior correctly

Screenshots (optional):

Screenshot 2026-01-23 at 12 35 57 PM

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

  • New Features

    • Runtime hook merges built-in and fetched edge categories for Explore pathfinding.
    • Added a frontend-specific Active Directory edge list to ensure schema alignment.
  • Improvements

    • Pathfinding filters: dynamic initialization, explicit empty-filter handling, and loading state exposed.
    • Shortest-path searches now consider only traversable edges for more accurate results.
  • API Enhancements

    • Client can fetch edge types and exposes related response types.

✏️ Tip: You can customize this high-level summary in your review settings.

@maffkipp maffkipp self-assigned this Jan 23, 2026
@maffkipp maffkipp added the user interface A pull request containing changes affecting the UI code. label Jan 23, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

Walkthrough

Generates a new frontend-facing AD pathfinding edges function, adds runtime fetching/merging of built-in + OpenGraph edge categories (useEdgeCategories), replaces static pathfinding filter defaults with dynamic initialization and empty-filter handling, and extends the client with getEdgeTypes + EdgeType response types.

Changes

Cohort / File(s) Summary
Schema generation & graph schema
packages/go/schemagen/generator/typescript.go, packages/javascript/bh-shared-ui/src/graphSchema.ts
Generator now emits ActiveDirectoryPathfindingEdgesMatchFrontend; graphSchema.ts exports that new frontend-facing AD edge function alongside existing edge functions.
Edge categories, mapping & utilities
packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/edgeCategories.tsx, .../EdgeFilter/utils.ts, .../EdgeFilter/edgeCategories.test.tsx
Renamed AllEdgeTypesBUILTIN_EDGE_CATEGORIES; added mapEdgeTypesToCategory, filterUnneededTypes, getEdgeListFromCategory, and BUILTIN_TYPES; added tests comparing category lists to schema functions.
Runtime category hook & tests
packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.ts, .../useEdgeCategories.test.ts
New useEdgeCategories hook reads opengraph_pathfinding feature flag, optionally fetches /api/v2/graph-schema/edges, filters/maps types, and merges an OpenGraph category with built-ins; returns isLoading, isError, and edgeCategories.
Pathfinding filters & helpers
packages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/pathfindingSearch.ts, .../queries/utils.ts, .../utils.ts, usePathfindingFilters.ts, usePathfindingFilters.test.tsx
Replaced static defaults with dynamic init using getInitialPathFilters(BUILTIN_EDGE_CATEGORIES) and useEdgeCategories; added areFiltersEmpty; query now includes computed filter and avoids default-filter fallbacks; usePathfindingFilters exposes isLoading.
EdgeFilter component & tests
packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/EdgeFilter.tsx, EdgeFilter.test.tsx, EdgeFilteringDialog.tsx, EdgeFilteringDialog.test.tsx
Switched imports to new edgeCategories module and useEdgeCategories; component renders categories from hook with a loading fallback; tests updated to new exports/paths.
Client API & response types
packages/javascript/js-client-library/src/client.ts, packages/javascript/js-client-library/src/responses.ts
Added getEdgeTypes() client method and EdgeType / GetEdgeTypesResponse types; getShortestPathV2 now requests only_traversable: true.
Exports & import relocations
packages/javascript/bh-shared-ui/src/index.ts, .../useExploreParams/useExploreParams.tsx, .../PathfindingSearch.tsx, multiple EdgeInfo files
Removed edgeTypes barrel export and moved several imports (EdgeCheckboxType, SelectedEdge) into the new edgeCategories module; updated relative import paths.
Tests & minor adjustments
various *.test.tsx files across EdgeFilter, usePathfindingFilters, EdgeInfo
Tests updated for async initialization, new exports, and category ↔ schema parity; added useEdgeCategories tests using MSW.
Config / formatting
packages/javascript/bh-shared-ui/tsconfig.json
EOF/newline formatting only (no semantic changes).

Sequence Diagram(s)

sequenceDiagram
    participant UI as EdgeFilteringDialog (UI)
    participant Hook as useEdgeCategories
    participant FeatureAPI as Feature Flag API
    participant SchemaAPI as Graph Schema API
    participant Query as react-query

    UI->>Hook: call useEdgeCategories()
    Hook->>Query: request feature flag (opengraph_pathfinding)
    Query->>FeatureAPI: evaluate flag
    FeatureAPI-->>Query: flag value
    Query-->>Hook: flag result
    alt feature enabled
        Hook->>Query: fetch /api/v2/graph-schema/edges
        Query->>SchemaAPI: GET /api/v2/graph-schema/edges
        SchemaAPI-->>Query: EdgeType[] response
        Query-->>Hook: edge types
        Hook->>Hook: filterUnneededTypes(...) & mapEdgeTypesToCategory(...)
        Hook->>Hook: merge OpenGraph category with BUILTIN_EDGE_CATEGORIES
    else feature disabled
        Hook->>Hook: use BUILTIN_EDGE_CATEGORIES only
    end
    Hook-->>UI: return { isLoading, isError, edgeCategories }
    UI->>UI: render categories or loading state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, api

Suggested reviewers

  • elikmiller
  • superlinkx

Poem

🐇 I hopped through edges both old and new,
Merged built-ins with OpenGraph too,
A frontend AD list now on parade,
Tests and hooks all neatly made,
🥕 Cheers — from your friendly CodeRabbit!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main feature being added (OpenGraph Edge Filtering) and references the associated ticket (BED-6725), accurately summarizing the primary change.
Description check ✅ Passed The description fully addresses the template requirements with comprehensive details about what was changed, why it was needed, testing coverage, and includes all checklist items marked as complete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

GenerateTypeScriptStringEnum(root, "ActiveDirectoryKindProperties", schema.Properties)

GenerateTypeScriptPathfindingEdgesFn(root, "ActiveDirectoryPathfindingEdges", "ActiveDirectoryRelationshipKind", schema.PathfindingRelationships)
GenerateTypeScriptPathfindingEdgesFn(root, "ActiveDirectoryPathfindingEdgesMatchFrontend", "ActiveDirectoryRelationshipKind", schema.PathfindingRelationshipsMatchFrontend)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding this list to TS schemagen output so that we can validate our hardcoded edge categories in the UI contain the correct edges.

} from './utils';

// Only need to create our default filters once
const DEFAULT_FILTERS = createPathFilterString(INITIAL_FILTER_TYPES);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Default filters are no longer needed due to the addition of the only_traversable query param that we now use to automatically filter out non-traversable edges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/javascript/bh-shared-ui/src/hooks/useExploreGraph/usePathfindingFilters.ts (1)

28-44: Gate initialize() to prevent incomplete edge filters during async category loading.

The filter button calls initialize() without checking isLoading. If clicked while edgeCategories are still loading, getInitialPathFilters(edgeCategories) operates on incomplete data (missing OpenGraph edges), resulting in incomplete filter options. Either disable the filter button until isLoading === false, or add a loading check inside initialize() to defer sync until categories load.

🤖 Fix all issues with AI agents
In
`@packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.test.ts`:
- Around line 62-67: The test is asserting objects but getEdgeListFromCategory
returns string[]; update the assertions to check string values directly: in
useEdgeCategories.test.ts replace the objectContaining check on edgeList with
direct string assertions (e.g., expect(edgeList).not.toContain(<edgeName>) or
expect(edgeList).toEqual(expect.arrayContaining([ 'edgeName1', 'edgeName2' ])))
and keep the length assertion; apply the same fix to the other occurrence around
lines 92-97 so all checks assert against edge names returned by
getEdgeListFromCategory(hook.result.current.edgeCategories).

In
`@packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.ts`:
- Around line 25-42: The memoized edgeCategories currently only depends on
edgeTypesQuery.data and can continue to append cached OpenGraph edges when the
feature flag toggles off; update the useMemo for edgeCategories to explicitly
check openGraphFeatureFlag?.enabled (in addition to filtering with
filterUneededTypes) before appending mapEdgeTypesToCategory results to
BUILTIN_EDGE_CATEGORIES, and add openGraphFeatureFlag?.enabled to the dependency
array so the memo recalculates when the flag changes; reference
useFeatureFlag/openGraphFeatureFlag, edgeTypesQuery, useMemo,
BUILTIN_EDGE_CATEGORIES, mapEdgeTypesToCategory, and filterUneededTypes when
making this change.
🧹 Nitpick comments (6)
packages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/pathfindingSearch.ts (1)

46-46: The enabled condition is redundant.

At line 32, you already return { enabled: false } when !primarySearch || !searchType || !secondarySearch. If execution reaches line 46, all three are guaranteed to be truthy, making this check always evaluate to true.

Suggested simplification
     return {
         ...sharedGraphQueryOptions,
         queryKey: [ExploreGraphQueryKey, searchType, primarySearch, secondarySearch, filter],
         queryFn: ({ signal }) => {
             return apiClient
                 .getShortestPathV2(primarySearch, secondarySearch, filter, { signal })
                 .then((res) => res.data);
         },
-        enabled: !!(searchType && primarySearch && secondarySearch),
+        enabled: true,
     };
packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/EdgeFilteringDialog.test.tsx (1)

71-75: Avoid brittle category indexing in tests.

Indexing BUILTIN_EDGE_CATEGORIES[0] assumes ordering; a category insert/reorder would break these assertions. Consider looking up by categoryName instead.

♻️ Suggested tweak
-        const activeDirectorySubcategories = BUILTIN_EDGE_CATEGORIES[0].subcategories;
+        const activeDirectorySubcategories =
+            BUILTIN_EDGE_CATEGORIES.find((c) => c.categoryName === 'Active Directory')?.subcategories ?? [];
@@
-        const edgeTypes = BUILTIN_EDGE_CATEGORIES[0].subcategories[0].edgeTypes;
+        const edgeTypes = activeDirectorySubcategories?.[0]?.edgeTypes ?? [];

Also applies to: 112-115

packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/edgeCategories.test.tsx (1)

37-42: Set.prototype.symmetricDifference requires Node 20+ and lacks TypeScript typings in versions <5.5.

The repository runs Node 22 in CI and targets ESNext, so the method works at runtime. However, the @ts-ignore suppresses a legitimate type error in TypeScript 5.1.6. This can be cleaned up by either upgrading TypeScript to 5.5+ or using a manual symmetric difference implementation to avoid the type suppression.

Manual implementation option
-function getDifferenceCount(a: string[] | undefined | null, b: string[] | undefined | null) {
-    const setA = new Set(a);
-    const setB = new Set(b);
-
-    // `@ts-ignore`: can be removed once typescript is upgraded to >= v5.5
-    return setA.symmetricDifference(setB).size;
-}
+function getDifferenceCount(a: string[] | undefined | null, b: string[] | undefined | null) {
+    const setA = new Set(a ?? []);
+    const setB = new Set(b ?? []);
+
+    let diff = 0;
+    for (const item of setA) if (!setB.has(item)) diff++;
+    for (const item of setB) if (!setA.has(item)) diff++;
+    return diff;
+}
packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/utils.ts (3)

22-43: Stabilize subcategory/edge ordering for consistent UI and tests.

API ordering isn’t guaranteed; preserving insertion order can lead to UI jitter and snapshot flakiness. If ordering isn’t meaningful, sort subcategories and edge names for determinism.

♻️ Suggested update
-    return {
-        categoryName,
-        subcategories: Array.from(subcategories.values()),
-    };
+    const sortedSubcategories = Array.from(subcategories.values())
+        .map((subcategory) => ({
+            ...subcategory,
+            edgeTypes: [...subcategory.edgeTypes].sort(),
+        }))
+        .sort((a, b) => a.name.localeCompare(b.name));
+
+    return {
+        categoryName,
+        subcategories: sortedSubcategories,
+    };

45-48: Fix exported API typo: filterUneededTypesfilterUnneededTypes.

Since this is a new export, it’s worth correcting now to avoid a permanent typo in the public surface. Update call sites accordingly.

✏️ Suggested rename
-export const filterUneededTypes = (data: EdgeType[] | undefined): EdgeType[] | undefined => {
+export const filterUnneededTypes = (data: EdgeType[] | undefined): EdgeType[] | undefined => {
     return data?.filter((edge) => !BUILTIN_TYPES.includes(edge.schema_name) && edge.is_traversable);
 };

50-54: Return an empty array when category is missing.

This avoids undefined propagation and simplifies callers; also makes the return type explicit.

✅ Safer return shape
-export const getEdgeListFromCategory = (categoryName: string, categories: Category[] = BUILTIN_EDGE_CATEGORIES) => {
-    const category = categories.find((category) => category.categoryName === categoryName);
-    return category?.subcategories.flatMap((subcategory) => subcategory.edgeTypes);
-};
+export const getEdgeListFromCategory = (
+    categoryName: string,
+    categories: Category[] = BUILTIN_EDGE_CATEGORIES
+): string[] => {
+    const category = categories.find((category) => category.categoryName === categoryName);
+    return category ? category.subcategories.flatMap((subcategory) => subcategory.edgeTypes) : [];
+};

const setA = new Set(a);
const setB = new Set(b);

const result = new Set([...[...setA].filter((x) => !setB.has(x)), ...[...setB].filter((x) => !setA.has(x))]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was hoping to use the new Set.symmetricDifference() method here but it is unfortunately not yet available in the JS version we are using in CI: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference


// this hook combines our hardcoded edge categories with an OpenGraph category pulled from the API
export const useEdgeCategories = () => {
const { data: openGraphFeatureFlag } = useFeatureFlag('opengraph_search');
Copy link
Contributor

Choose a reason for hiding this comment

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

note: once the opengraph_pathfinding feature flag is added, this will need to be switched over to use that feature flag.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for doing this, hopefully will prevent some future bugs! 🙌

return {
data: [
{
key: 'opengraph_search',
Copy link
Contributor

Choose a reason for hiding this comment

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

note: this will also need to be updated to opengraph_pathfinding once the backend is updated

};

// removes all built-in and non-traversable edges from a list of edge types
export const filterUneededTypes = (data: EdgeType[] | undefined): EdgeType[] | undefined => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit (non-blocking): typo in this function name, should probably beunneeded

Copy link
Contributor

@sirisjo sirisjo left a comment

Choose a reason for hiding this comment

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

Tested locally, this looks good to me!

@maffkipp maffkipp merged commit e84df82 into main Jan 28, 2026
13 checks passed
@maffkipp maffkipp deleted the BED-6725 branch January 28, 2026 22:49
@github-actions github-actions bot locked and limited conversation to collaborators Jan 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

user interface A pull request containing changes affecting the UI code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants