Skip to content

ENG-3767: Land DSR traversal visualizer (initial merge from PoC)#8168

Merged
JadeCara merged 35 commits into
mainfrom
gill/ENG-3767/merge-dsr-traversal
May 19, 2026
Merged

ENG-3767: Land DSR traversal visualizer (initial merge from PoC)#8168
JadeCara merged 35 commits into
mainfrom
gill/ENG-3767/merge-dsr-traversal

Conversation

@gilluminate
Copy link
Copy Markdown
Contributor

@gilluminate gilluminate commented May 12, 2026

Ticket ENG-3767

Description Of Changes

Companion PR: fidesplus#3572

Lands the DSR traversal visualizer in main for the first time.

About the diff size. The PoC originated as a fast-moving, largely AI-generated branch (#8099) that wasn't given a substantive code review before it landed on its source branch. The branch on this PR includes both the cherry-pick of that PoC and a polish pass that applies our frontend conventions and addresses a first review pass before merge. The intent is that what you're reviewing is the version of the feature we'd actually want to ship — not the PoC's raw state.

Diffing this PR against the PoC will show substantive differences: string-union types replaced with enums, default exports flipped to named, hex literals swapped for palette tokens, antd v6 deprecations fixed, RTK Query cache key bug fixed, hydration-snap on first paint fixed, CodeQL finding on hand-built routes fixed, and more. Please review the final state rather than trying to diff-the-diff. The polish commit history is intact if you want to see what was changed and why.

Feature summary. A property-scoped, action-typed preview of how a DSR will fan out through configured integrations. Renders an interactive four-lane graph (identity input → systems queried → manual review gates → not touched) with per-card metadata, dataset breakdowns, and dependency edges. Gated behind the new dsrTraversalVisualizer beta flag (dev + test on, production off) and reachable at /dsr-traversal/[propertyKey]/[[...actionType]] from the "Request workflows" item in the Privacy requests nav group.

Code Changes

Backend (src/fides/api/graph/preview/)

  • builder.pyTraversalPreviewBuilder builds the structured preview, falling back to FK-derived edges when full traversal fails
  • reachability.py — per-integration reachability classification
  • policy_filter.py — policy-aware data-category filtering with descendant matching
  • schemas.py — Pydantic response shapes
  • tests/ops/graph/preview/ — builder + reachability tests with shared conftest.py fixtures
  • Code review fixes: scale optimization for _static_dataset_detail (O(N×M) → O(1) via indexes), custom category dot-prefix fallback in policy_filter.py, warning surfacing from _capture_traversal, deduplication (graph.identity_keys, _edges_from_counts), modern typing aliases

Frontend (clients/admin-ui/src/features/dsr-traversal-visualizer/)

  • Page + canvas (ReactFlow-based four-lane layout)
  • Three node types (identity root, integration, manual task)
  • Two edge types (dependency, gates)
  • Two side panels (integration details, manual task details)
  • Custom four-lane layout (layout/compute-lane-layout.ts, compute-stages.ts, compute-column-count.ts) — pure functions with unit tests
  • RTK Query slice with serializeQueryArgs that strips refresh so regenerate updates the same cache entry
  • Hooks: useLaneCollapseState (persists collapse prefs to localStorage, exposes a hydrated flag to gate first-paint and avoid layout snap), useNodeSelection, useTraversalGraph
  • Route shells in clients/admin-ui/src/pages/dsr-traversal/
  • Type-safe enums for ActionType, Reachability, ActionStatus, LaneId; REACHABILITY_COLOR typed against CUSTOM_TAG_COLOR

MSW handlers (clients/admin-ui/src/mocks/dsr-traversal/)

  • Self-contained fixture rich enough to exercise all card states (stage-1 + stage-2 reachable, gated by manual review, not-touched) via npm run dev:mock without seeding the fidesplus DB

Flag + nav wiring

  • dsrTraversalVisualizer flag in flags.json (dev + test on, production off)
  • nav-config.tsx entry under "Privacy requests → Request workflows", gated by the flag + requiresPlus

Steps to Confirm

The visualizer is gated behind the dsrTraversalVisualizer flag (on by default in dev/test, off in production).

  1. Smoke against mock data (no backend setup required):
    cd clients/admin-ui && npm run dev:mock
    
    Navigate to Privacy requests → Request workflows in the sidebar. The MSW handlers seed one property ("Mocked Property") with 5 in-flow integrations across 2 stages, 1 manual review task that gates the legal archive, and 2 not-touched systems.
  2. Select the property in the picker → confirm the four-lane layout renders with stage 1 / stage 2 grouping in "Will be queried", the Legal review task in "Gated by manual review", and (with the Show not touched toggle on) Legacy ERP + Vendor X in the "Not touched" lane.
  3. Click an integration card → side panel opens with system, triggered-by sources, manual-review gate (if any), and dataset/collection/field breakdown.
  4. Click Regenerate → confirm a single traversal-preview?refresh=true network call fires and writes back into the same cache entry (no parallel cache entries).
  5. Toggle Access / Erasure → URL updates and the preview refreshes.
  6. Collapse a lane via the floating chevron button → preference persists across reload (localStorage key fides:dsr-traversal:lane-collapse:v1); on reload the canvas renders with the persisted layout from the first paint (no visible re-layout snap).
  7. Backend smoke (optional, requires fidesplus + seeded property/integrations): hit GET /api/v1/plus/properties/{property_id}/traversal-preview?action_type=access&include_unreachable=true and confirm the response matches the TraversalPreview schema; pass refresh=true to bypass cache.

Known Follow-ups (deferred)

From an in-session /code-review pass, these were judged worth deferring rather than blocking merge:

  • snake_case wire-format names leak into TS internals (~24 identifiers across the feature). Normalize at the slice boundary via transformResponse in a follow-up — same pattern affects other features in the repo.
  • compute-stages BFS depth-tracking deserves a diamond-with-extra-hop regression test. Practical case appears to be correct; a test would lock it in.
  • Custom category matching semantics: preview uses taxonomy walk + dot-prefix fallback, real DSR uses bare startswith. Aligned for custom categories but user_provided edge case differs (preview is stricter/more correct).

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • Documentation issue created in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

🤖 Generated with Claude Code

JadeCara and others added 14 commits May 11, 2026 16:21
Co-Authored-By: mfbrown <michael.brown@ethyca.com>
Co-Authored-By: Lucano Vera <lucanovera@live.com.ar>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply post-cherry-pick code-quality pass on the DSR traversal visualizer:

- Replace inline-styled `<div>` containers with `<Flex>`; extract chrome
  styling (sticky header, gating-task pill) into SCSS modules using
  fidesui palette tokens instead of hardcoded hex colors.
- Convert template-string classnames to `classNames()`.
- Introduce enums for `ActionType`, `Reachability`, `ActionStatus`, and
  `LaneId` (moved from `constants.ts` to `types.ts`); update all
  consumers + tests. `REACHABILITY_COLOR` now typed against
  `CUSTOM_TAG_COLOR`.
- Replace `as any` test casts with a typed `buildIntegration()` fixture
  helper. Fix pre-existing fixture bug (`property.key` -> `property.id`).
- Switch component default exports to named exports across the feature;
  Next.js page wrappers retain default export but use named imports.
- Replace `propertyKey!` non-null assertion with RTK Query `skipToken`.
- Fix antd v6 deprecation: `Select` `optionFilterProp` -> `showSearch`
  object config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a self-contained fixture and handlers so the DSR traversal
visualizer can be exercised via `npm run dev:mock` without seeding
the fidesplus DB. Fixture covers all card lanes (stage-1 + stage-2
reachable, gated, not-touched), an inter-stage dependency edge with
dep_count > 1, and a manual review task that gates one integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dependency edge legend swatch was a solid blue line, but the
actual edges render dark dashed (react-flow's animated style). Use
minos for the stroke + a dashed pattern in the legend swatch so the
key matches what users see on the canvas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace canvas height calc with flex:1 so the visualizer fills the
  remaining viewport height naturally.
- Add a PageHeader matching the "Request workflows" nav item; move
  controls (property picker, action toggle, show-not-touched, summary)
  below it.
- Drop the sticky positioning, horizontal padding, background, and
  bottom border from the header; consolidate the header styles into
  Tailwind utility classes and remove the now-unused SCSS module.
- Wrap the canvas content in an antd Card so it gets a uniform border
  + rounded corners on all four sides without hand-rolling CSS.
- Replace the text "Regenerate" button with an icon-only button using
  Icons.Renew + a tooltip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mary

Reserve the summary row's vertical space so the page doesn't jump when
the first preview loads, and swap the comma-separated string for the
codebase's standard `Statistic + Text` stat pattern (as used in
DSRStatusCard and AstralisPanel). Numbers now read with proper visual
weight instead of inline bold inside secondary-colored text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hide the "Request workflows" nav item (and the routes it points to)
behind a new beta feature flag. Defaults to on in dev/test and off in
production, matching the rollout pattern used by dataCatalog,
webMonitor, policies, and other recently-shipped beta features.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…kens

LaneChrome and ManualTaskNode SCSS modules used raw hex colors
throughout, violating the styling priority that requires palette
tokens, antd CSS vars, or fidesui global vars. Swap every literal
for the appropriate token: success-bg/border + success-text-active
for the reach lane and stage labels; warning-bg/border for the gated
lane and manual-task chrome; brand-bg-sandstone for the identity
lane; fill-quaternary + neutral border for the skipped lane;
color-text-tertiary for rail/chevron strokes; bg-container for white
surfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace manual filter+join class-string construction with the
classnames package's object syntax, matching the convention already
used in ManualTaskNode and LegendPanel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The traversal-preview slice keyed its cache on the full args including
`refresh`, so the regenerate button (which passes `refresh: true`)
populated a parallel cache entry instead of replacing the existing
one. Strip `refresh` from the serialized key via `serializeQueryArgs`
and force the network round-trip with `forceRefetch` when the caller
opts in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ydrate

The lane-collapse hook starts with hardcoded DEFAULTS for SSR safety,
then swaps in the localStorage value in a mount effect. When stored
prefs differ from defaults, the first ReactFlow paint uses the wrong
lane state and the FitViewOnLayoutChange animation visibly snaps to
the correct layout on the next tick.

Have the hook expose a `hydrated` flag that flips true once the mount
effect runs. TraversalCanvas renders a Spin placeholder until then.
First paint of ReactFlow now matches localStorage so no re-layout
animation fires on load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eight

NODE_HEIGHT was a fiction: its comment claimed 160 was the rendered
card height, but the IntegrationNode SCSS set min-height to 210 and
CARD_PITCH was derived from NODE_HEIGHT + 70. Three hand-tuned
numbers that had to harmonize, plus dead dagre-based layout code
(layout-utils.ts) that nothing imported.

Replace with:
- INTEGRATION_CARD_MIN_HEIGHT (210) -- the actual minimum.
- INTER_CARD_GAP (20) -- vertical gap between cards.
- CARD_PITCH = INTEGRATION_CARD_MIN_HEIGHT + INTER_CARD_GAP -- derived.

IntegrationNode applies min-height inline from the TS constant so
the SCSS can't drift from the layout math. layout-utils.ts deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `_refresh` destructure (rejected by naming-convention rule)
with a shallow-copy + delete in serializeQueryArgs, and apply prettier
formatting on the header summary row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
fides-plus-nightly Ignored Ignored Preview May 19, 2026 9:42pm
fides-privacy-center Ignored Ignored May 19, 2026 9:42pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

Title Lines Statements Branches Functions
admin-ui Coverage: 10%
7.77% (3620/46564) 6.99% (1889/27010) 5.16% (708/13709)
fides-js Coverage: 78%
79.17% (1977/2497) 66.25% (1249/1885) 73.31% (349/476)
privacy-center Coverage: 85%
82.53% (364/441) 79.74% (189/237) 74.07% (60/81)

…query

CodeQL flagged the hand-built route string in TraversalVisualizerPage's
`goTo` as DOM text reinterpreted as HTML -- the property key from the
picker was being interpolated directly into the URL, which would let a
malformed key smuggle path separators or fragments. Use the existing
`DSR_TRAVERSAL_PROPERTY_ACTION_ROUTE` constant with router.replace's
pathname+query form so Next.js does the substitution and encoding.

Also addresses code-review item #7 from the earlier session pass (use
the route constant instead of hand-building paths).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gilluminate gilluminate changed the title ENG-3767: Merge DSR traversal visualizer (cherry-pick + polish) ENG-3767: Land DSR traversal visualizer (initial merge from PoC) May 12, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 0% with 284 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.10%. Comparing base (d096049) to head (64a032b).

Files with missing lines Patch % Lines
src/fides/api/graph/preview/builder.py 0.00% 162 Missing ⚠️
src/fides/api/graph/preview/schemas.py 0.00% 79 Missing ⚠️
src/fides/api/graph/preview/policy_filter.py 0.00% 27 Missing ⚠️
src/fides/api/graph/preview/reachability.py 0.00% 16 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8168      +/-   ##
==========================================
- Coverage   85.66%   85.10%   -0.57%     
==========================================
  Files         665      669       +4     
  Lines       43086    43370     +284     
  Branches     5041     5080      +39     
==========================================
  Hits        36911    36911              
- Misses       5067     5351     +284     
  Partials     1108     1108              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

JadeCara and others added 2 commits May 13, 2026 16:18
…st coverage

- Fix changelog: remove incorrect db-migration label
- Move local import to top level (reachability → builder)
- Modernize typing: Dict→dict, List→list, Optional→X|None (all 4 files)
- Document skipped field contract on CollectionDetail schema
- Surface traversal warnings instead of swallowing them silently
- Deduplicate: _dataset_to_integration computed once, _edges_from_counts
  extracted, graph.identity_keys replaces manual field walking
- Scale: _static_dataset_detail O(N×M)→O(1) via _dataset_nodes and
  _integration_datasets indexes; _static_edges identity loop replaced
  with set comprehension over graph.identity_keys
- Fix custom category matching: dot-prefix fallback for categories not
  in DEFAULT_TAXONOMY so preview matches real DSR runner behavior
- Add test_policy_filter.py (10 tests including custom category coverage)
- Add 6 builder tests (fallback edges, static detail, warnings, empty
  graph, target categories, skip_processing rename)
- Add 2 reachability tests (REQUIRES_MANUAL_IDENTITY, multi-dataset wins)
- Rename misleading test to test_graph_excludes_skip_processing_collections

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eachable nodes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gilluminate gilluminate marked this pull request as ready for review May 13, 2026 22:48
@gilluminate gilluminate requested review from a team as code owners May 13, 2026 22:48
@gilluminate gilluminate requested review from adamsachs and removed request for a team May 13, 2026 22:48
gilluminate and others added 3 commits May 18, 2026 12:00
Drops the IntegrationDetailPanel SCSS module entirely: the
``Manual review required`` blocks now render as warning Alerts
instead of hand-rolled div + badge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the custom centered secondary-text empty state with Ant's
``<Empty>`` component, which renders the standard illustration plus
the prompt to pick a property.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the ``via {stage_via}`` line into the header label column so it
shares the logo's left offset, top-align the logo with the first text
row, and drop SCSS rules now covered by ``<Text type=\"secondary\">``.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 18, 2026

Dependency Review

✅ No vulnerabilities found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 64a032b.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

  • clients/admin-ui/package.json
  • clients/package-lock.json

Regenerated TS types now that the fidesplus traversal-preview endpoint
returns typed Pydantic models. Adds the 15 transitive generated schemas
plus barrel re-exports, and reduces the feature's types.ts to thin
wrappers: re-exports for 1:1 shapes, Omit-extends for the React Flow
``Data`` wrappers and for list fields that Pydantic ``default_factory=list``
emits as optional in OpenAPI but always populates at runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update the schema, generated types, mock data, and panel component
to support multiple data uses per system in the DSR traversal preview.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JadeCara and others added 2 commits May 19, 2026 10:54
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JadeCara JadeCara added this pull request to the merge queue May 19, 2026
Merged via the queue into main with commit 86ff003 May 19, 2026
73 of 76 checks passed
@JadeCara JadeCara deleted the gill/ENG-3767/merge-dsr-traversal branch May 19, 2026 22:04
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.

4 participants