Skip to content

Add endpoint to retrieve the full score set supersession chain #709

@bencap

Description

@bencap

Summary

Score sets form supersession chains via a doubly-traversable linked list (superseded_score_set / superseding_score_set relationships). Currently, the API only exposes one hop in each direction on the score set detail response. There is no way to retrieve the full ordered chain without the client walking it manually with N+1 requests.

Problem

When a user views a score set that is part of a supersession chain, the frontend can only show the immediate predecessor and successor. To display the full version history (e.g. "version 2 of 4" or a timeline of all versions), the client must:

  1. Fetch the current score set
  2. Follow superseded_score_set.urn backward, fetching each predecessor
  3. Follow superseding_score_set.urn forward, fetching each successor
  4. Assemble the chain client-side

This is fragile, slow, and requires the client to implement chain-walking logic that already exists server-side in lib/score_sets.py.

Proposed behavior

Add a GET /score-sets/{urn}/supersession-chain endpoint that returns the full ordered supersession chain for the given score set. The response should be an ordered list from oldest to newest, using ShortScoreSet for each entry, plus metadata indicating which entry in the chain corresponds to the requested URN.

ShortScoreSet includes urn, title, short_description, and published_date — sufficient for the frontend to render a rich version timeline without follow-up requests.

Example response shape:

{
  "current_urn": "urn:mavedb:00000001-a-2",
  "chain": [
    {
      "urn": "urn:mavedb:00000001-a-1",
      "title": "Original submission",
      "short_description": "Initial DMS data for BRCA1",
      "published_date": "2024-03-15",
      "record_type": "Score Set"
    },
    {
      "urn": "urn:mavedb:00000001-a-2",
      "title": "Updated scoring",
      "short_description": "Corrected normalization",
      "published_date": "2024-09-01",
      "record_type": "Score Set"
    },
    {
      "urn": "urn:mavedb:00000001-a-3",
      "title": "GRCh38 remapping",
      "short_description": "Remapped to GRCh38 reference",
      "published_date": null,
      "record_type": "Score Set"
    }
  ]
}

Permission-aware: entries the requesting user cannot read should be omitted from the chain, consistent with existing behavior in find_superseded_score_set_tail.

Acceptance criteria

  • GET /score-sets/{urn}/supersession-chain returns the full ordered chain from oldest to newest
  • Each entry in the chain uses ShortScoreSet (urn, title, short_description, published_date, record_type)
  • current_urn indicates which entry in the chain was requested
  • Chain entries the user does not have read permission for are omitted
  • A score set with no supersession relationships returns a single-element chain (itself)
  • The endpoint returns 404 if the requested URN does not exist or the user cannot read it

Implementation notes

  • Chain traversal logic already exists in lib/score_sets.py (find_superseded_score_set_tail, find_publish_or_private_superseded_score_set_tail). These walk forward via superseding_score_set. The new endpoint needs to walk both directions from the requested URN: backward via superseded_score_set to find the chain head, then forward via superseding_score_set to build the full ordered list.
  • The existing traversal functions interleave permission checks with chain walking. The new endpoint should follow the same pattern — skip entries the user cannot read rather than terminating the walk.
  • A new response model will be needed (e.g. SupersessionChainResponse with current_urn: str and chain: list[ShortScoreSet]), defined in view_models/score_set.py.
  • ShortScoreSet already exists in view_models/score_set.py and includes the fields needed for a rich timeline display. No changes to this model are required.
  • Ensure relationships are eagerly loaded to avoid N+1 queries during traversal. Consider using a recursive CTE if chains become long enough to warrant it, though the linked-list walk is likely sufficient for typical chain lengths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    app: backendTask implementation touches the backendtype: enhancementEnhancement to an existing feature

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions