feat(versioning): role-aware family collapse, family-latest resolver, EntityVersionPicker (PRD #442 phase 3)#467
Merged
Conversation
… 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.
…ck (#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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #448, which was auto-closed when its original stacked base branch was deleted during the #447 admin merge. Identical content — same two commits rebased onto current
main.Summary
Phase 3 of PRD #442. Closes the gap between "which row collapses to the latest in a family" and "who's allowed to see what", and ships the unified
EntityVersionPickerthat the link-contract-to-port dialog (and future selectors per #452/#453/#454) already consume.Backend
common/version_visibility.py(new) — role-aware status rank tables +collapse_by_familyhelper. Consumer rank treatsdraftas invisible; elevated rank (admin / owner / subscriber / team-member) treatsdraftas a real version.DataContractsManager/DataProductsManager: integrate the helper intolist_*paths, threadingcaller_email+caller_team_idsthrough so the manager can compute_elevated_*_familiesonce per request.GET /api/data-contracts/families/{family_id}/latestandGET /api/data-products/families/{family_id}/latest— visibility-aware resolvers for family-follow-latest references.serialization_aliasforversionFamilyId,versionCount,parentContractId,baseName,changeSummary,draftOwnerIdso the API actually emits camelCase to match the frontend types.Frontend
components/common/entity-version-picker.tsx(new) — unified searchable combobox supportingentity-pinnedandfamily-follow-latestscopes. When astatusFilteris set, falls back to?include_history=trueand collapses client-side so published versions inside an otherwise-draft-latest family stay selectable.link-contract-to-port-dialog.tsx: migrated from bespokeSelecttoEntityVersionPicker(closes [Feature]: UI support for contract versions #69).version-selector.tsxshared-component touch-up: passes the full row toonVersionChange, addstriggerClassNamefor layout reuse.Tests
test_version_visibility.py— status visibility, rank ordering, tie-breaks.test_version_family.py— manager-level collapse with elevated/consumer modes.test_data_contracts_api_models.py— camelCase emission for the aliased fields.33 unit tests across
test_version_family.py+test_version_visibility.pypass on the rebased-to-main branch.Stack note
Has one dependent PR (#457, clone-serialization fix for #455) — that PR's base will be retargeted to
mainimmediately after this one merges.Test plan
pytest src/tests/unit/test_version_family.py src/tests/unit/test_version_visibility.py) — 33/33 pass.