Skip to content

feat(versioning): list-view collapse with include_history toggle (Phase 2)#447

Merged
larsgeorge-db merged 1 commit into
mainfrom
feat-version-family-listviews
May 28, 2026
Merged

feat(versioning): list-view collapse with include_history toggle (Phase 2)#447
larsgeorge-db merged 1 commit into
mainfrom
feat-version-family-listviews

Conversation

@larsgeorge-db
Copy link
Copy Markdown
Collaborator

Summary

Phase 2 of the unified version family work (PRD #442). Makes the data-contracts and data-products list views collapse by version_family_id by default, with a "Show all versions" toggle to expand. Each collapsed row gains a small "N versions" badge so users can see at a glance which entities have history.

Stacked on #446 (Phase 1). Base branch is `feat-version-family` — please merge #446 first.

Backend

  • DataContractSummary and DataProduct API models add versionCount (alias version_count). Emitted only on the collapsed view so the expanded view never reports misleading per-row counts.
  • DataContractsManager._query_contracts: drop the legacy base_name Python-side dedupe in favor of canonical version_family_id grouping; return (rows, family_counts) so the builder attaches counts without a second query.
  • _build_contract_summaries now also populates parentContractId, versionFamilyId, baseName, changeSummary, draftOwnerId on summary rows (these existed on the model but were always null in the response).
  • list_contracts_from_db + /data-contracts route propagate include_history end-to-end.
  • DataProductsManager.list_products: collapse by version_family_id after the visibility cascade, attach version_count to the surviving row; include_history=True bypasses the collapse and suppresses the count.
  • /data-products route surfaces include_history.

Frontend

  • DataContractListItem and DataProduct gain versionCount.
  • New components/common/version-count-badge.tsx — compact pill that hides itself for single-version families and otherwise renders a click-through history hint next to the version cell.
  • data-contracts.tsx and data-products.tsx: family badge next to the version column + "Show all versions" switch in the toolbar. Both list-fetch paths plumb include_history through the query string.

Tests

  • 5 new unit tests cover collapse default, version-count emission, include_history bypass on contracts, and product-side parity. Suite is now 17 → 22 passing.

Test plan

  • Unit tests pass: 22 / 22 in test_version_family.py
  • Linter clean on touched frontend files
  • Manual: restart backend → verify default list view shows one row per family, badge appears for families > 1 version
  • Manual: flip "Show all versions" toggle → all versions appear, badge disappears
  • Manual: clone a contract / product, confirm the source row collapses to the new clone with a "2" badge
  • Manual: consumer (non-admin) sees fewer rows; visibility-filtered count matches the badge
  • Manual: contracts with ?domain_id= still respect the collapse

Notes

  • The local dev server's auto-reloader has a pre-existing --reload-dir pointing at the old src/api layout (visible in /tmp/backend.log as a FileNotFoundError from statreload). A backend restart is required to pick up the new wiring for live verification — unrelated to this PR.

@larsgeorge-db larsgeorge-db requested a review from a team as a code owner May 27, 2026 12:17
@larsgeorge-db larsgeorge-db force-pushed the feat-version-family branch from 363467b to 0d2da07 Compare May 28, 2026 15:02
@larsgeorge-db larsgeorge-db force-pushed the feat-version-family-listviews branch from bc07763 to fb74752 Compare May 28, 2026 15:06
larsgeorge-db added a commit that referenced this pull request May 28, 2026
… EntityVersionPicker (PRD #442 phase 3)

Phase 3 of the unified version-family work. Three vertically-aligned slices:

1. **Role-aware visibility ranking** — adds
   `src/common/version_visibility.py` with elevated vs consumer status
   rank tables and a `collapse_by_family` helper. Plumbed into the
   contracts and products list managers so the family representative is
   picked according to the caller's role:
     * Consumer: ACTIVE > DEPRECATED (drafts hidden entirely)
     * Owner / team member / subscriber / admin: DRAFT > PROPOSED >
       UNDER_REVIEW > APPROVED > ACTIVE > DEPRECATED
   Subscription-based elevation is wired in for products via the
   existing `DataProductSubscriptionDb` table; contracts use ownership
   only (no subscription table today).

2. **Family-latest resolver endpoints** —
   `GET /api/data-contracts/families/{family_id}/latest` and the
   products counterpart. These resolve a family-follow-latest reference
   to a concrete row using the same role-aware rank, so any caller
   storing only a family id can read back the right version.

3. **EntityVersionPicker** — new shared component in
   `components/common/entity-version-picker.tsx`. Combobox over the
   collapsed list endpoint (one row per family with inline version
   badges), optional Entity-pinned / Family-follow-latest scope toggle,
   and an inline sub-picker for refining the version when pinning. The
   `link-contract-to-port-dialog` (closes #69) is migrated as the first
   call site, with `allowedScopes=['entity']` until the output-port
   reference-storage slice lands.

Tests added:
* `test_version_visibility.py` — 12 cases covering status visibility,
  rank ordering, tie-breaks, and the consumer-only / admin-only edges.
* `test_version_family.py` — two new manager-level cases proving
  consumers see the active rep while team members of the same family
  see the in-flight draft.

Note: the dev backend reloader has a pre-existing misconfiguration
(noted in PR #447). Logic is covered by unit tests; live verification
requires a manual `uvicorn` restart.
@larsgeorge-db larsgeorge-db changed the base branch from feat-version-family to main May 28, 2026 15:09
…se 2)

Make the data-contracts and data-products list views show one row per
version_family_id by default, with a "Show all versions" toggle that
expands every visible version. Each collapsed row surfaces a small
"N versions" badge so users can see at a glance which entities have
history, without paying the row-count cost.

Builds on PRD #442 / PR #446 (Phase 1).

Backend
- DataContractSummary + DataProduct API models gain a `versionCount`
  (alias `version_count`) field. Populated only on the collapsed view
  so the expanded view never lies about per-row counts.
- DataContractsManager._query_contracts: drop the legacy base_name
  Python-side dedupe in favor of canonical version_family_id grouping,
  and return (rows, family_counts) so the builder can attach counts
  without a second query. Same shape feeds both collapsed and
  include_history paths.
- DataContractsManager._build_contract_summaries now accepts an
  optional family_counts map and emits parent/family/draft/changeSummary
  fields that were previously left null on the summary even though they
  exist on the row.
- list_contracts_from_db and the /data-contracts route propagate the
  include_history flag end-to-end (route was already accepting it).
- DataProductsManager.list_products: collapse by version_family_id
  after the visibility cascade, attach version_count to the surviving
  row. include_history=True bypasses the collapse and suppresses the
  count.
- /data-products route surfaces include_history.

Frontend
- DataContractListItem and DataProduct gain `versionCount`.
- New components/common/version-count-badge.tsx — compact pill that
  hides itself for single-version families and otherwise renders a
  click-through history hint next to the version cell.
- data-contracts.tsx and data-products.tsx: family badge next to the
  version column + "Show all versions" switch in the toolbar; both
  list-fetch paths plumb include_history through the query string.

Tests
- 5 new unit tests cover the collapse default, the family-count emission,
  include_history bypass, and product-side parity.

Note: existing dev uvicorn instances have a stale --reload-dir pointing
at the pre-Phase-1 src/api layout (see /tmp/backend.log statreload trace).
Restart the backend to pick up the new include_history wiring locally.
@larsgeorge-db larsgeorge-db force-pushed the feat-version-family-listviews branch from fb74752 to 0fd78e4 Compare May 28, 2026 15:10
@larsgeorge-db larsgeorge-db merged commit d42e705 into main May 28, 2026
5 of 7 checks passed
@larsgeorge-db larsgeorge-db deleted the feat-version-family-listviews branch May 28, 2026 15:10
larsgeorge-db added a commit that referenced this pull request May 28, 2026
… EntityVersionPicker (PRD #442 phase 3)

Phase 3 of the unified version-family work. Three vertically-aligned slices:

1. **Role-aware visibility ranking** — adds
   `src/common/version_visibility.py` with elevated vs consumer status
   rank tables and a `collapse_by_family` helper. Plumbed into the
   contracts and products list managers so the family representative is
   picked according to the caller's role:
     * Consumer: ACTIVE > DEPRECATED (drafts hidden entirely)
     * Owner / team member / subscriber / admin: DRAFT > PROPOSED >
       UNDER_REVIEW > APPROVED > ACTIVE > DEPRECATED
   Subscription-based elevation is wired in for products via the
   existing `DataProductSubscriptionDb` table; contracts use ownership
   only (no subscription table today).

2. **Family-latest resolver endpoints** —
   `GET /api/data-contracts/families/{family_id}/latest` and the
   products counterpart. These resolve a family-follow-latest reference
   to a concrete row using the same role-aware rank, so any caller
   storing only a family id can read back the right version.

3. **EntityVersionPicker** — new shared component in
   `components/common/entity-version-picker.tsx`. Combobox over the
   collapsed list endpoint (one row per family with inline version
   badges), optional Entity-pinned / Family-follow-latest scope toggle,
   and an inline sub-picker for refining the version when pinning. The
   `link-contract-to-port-dialog` (closes #69) is migrated as the first
   call site, with `allowedScopes=['entity']` until the output-port
   reference-storage slice lands.

Tests added:
* `test_version_visibility.py` — 12 cases covering status visibility,
  rank ordering, tie-breaks, and the consumer-only / admin-only edges.
* `test_version_family.py` — two new manager-level cases proving
  consumers see the active rep while team members of the same family
  see the in-flight draft.

Note: the dev backend reloader has a pre-existing misconfiguration
(noted in PR #447). Logic is covered by unit tests; live verification
requires a manual `uvicorn` restart.
larsgeorge-db added a commit that referenced this pull request May 28, 2026
… EntityVersionPicker (PRD #442 phase 3) (#467)

* feat(versioning): role-aware family collapse, family-latest resolver, EntityVersionPicker (PRD #442 phase 3)

Phase 3 of the unified version-family work. Three vertically-aligned slices:

1. **Role-aware visibility ranking** — adds
   `src/common/version_visibility.py` with elevated vs consumer status
   rank tables and a `collapse_by_family` helper. Plumbed into the
   contracts and products list managers so the family representative is
   picked according to the caller's role:
     * Consumer: ACTIVE > DEPRECATED (drafts hidden entirely)
     * Owner / team member / subscriber / admin: DRAFT > PROPOSED >
       UNDER_REVIEW > APPROVED > ACTIVE > DEPRECATED
   Subscription-based elevation is wired in for products via the
   existing `DataProductSubscriptionDb` table; contracts use ownership
   only (no subscription table today).

2. **Family-latest resolver endpoints** —
   `GET /api/data-contracts/families/{family_id}/latest` and the
   products counterpart. These resolve a family-follow-latest reference
   to a concrete row using the same role-aware rank, so any caller
   storing only a family id can read back the right version.

3. **EntityVersionPicker** — new shared component in
   `components/common/entity-version-picker.tsx`. Combobox over the
   collapsed list endpoint (one row per family with inline version
   badges), optional Entity-pinned / Family-follow-latest scope toggle,
   and an inline sub-picker for refining the version when pinning. The
   `link-contract-to-port-dialog` (closes #69) is migrated as the first
   call site, with `allowedScopes=['entity']` until the output-port
   reference-storage slice lands.

Tests added:
* `test_version_visibility.py` — 12 cases covering status visibility,
  rank ordering, tie-breaks, and the consumer-only / admin-only edges.
* `test_version_family.py` — two new manager-level cases proving
  consumers see the active rep while team members of the same family
  see the in-flight draft.

Note: the dev backend reloader has a pre-existing misconfiguration
(noted in PR #447). Logic is covered by unit tests; live verification
requires a manual `uvicorn` restart.

* fix(versioning): camelCase API output + picker include_history fallback (#442 phase 3 follow-up)

Two issues uncovered during Playwright smoke testing of #448:

1. **camelCase serialization on list endpoints.** The
   ``DataContractSummary`` and ``DataProduct`` Pydantic models declared
   their PRD-#442 fields (``versionFamilyId``, ``versionCount``,
   ``parentContractId/parentProductId``, ``baseName``, ``changeSummary``,
   ``draftOwnerId``) with snake_case ``alias``-es. FastAPI's default
   ``response_model_by_alias=True`` then emitted snake_case, but the FE
   TS types use camelCase — so ``row.versionCount`` was silently
   ``undefined`` and the count badge never rendered. Switch each field
   to use Pydantic v2's ``serialization_alias`` so the wire format is
   camelCase regardless of the by-alias toggle, while the existing
   snake_case ``alias`` keeps ORM ``from_attributes=True`` reading
   working for both the SQLAlchemy attribute and snake_case JSON input.

2. **Picker hid valid published versions inside elevated families.**
   With role-aware ranking landed in #448, a family whose newest row is
   a draft now collapses to that draft as its representative. The
   EntityVersionPicker only ever saw collapsed reps, so a port-link
   dialog scoped to ``statusFilter=['active','approved','certified']``
   would silently drop families that had a perfectly valid published
   version available — e.g. Customer Data Contract disappeared from the
   POS Transaction Stream port picker after a 2.0.0 draft was created.
   When ``statusFilter`` is supplied, fetch with
   ``?include_history=true`` and collapse client-side after status
   filtering, preferring the newest matching version per family.
   ``versionCount`` is computed BEFORE collapse so the badge still
   reflects the full family size.

Smoke-tested end-to-end via Playwright MCP against a multi-version
family. Original v1.0.0 (active) is now selectable, picker write
persists the entity-pinned contract id correctly.
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.

1 participant