Skip to content

Migrate legacy Org compatibility to a BaseOrg-backed mask #1942

Description

@david-rocca

Goal

Make BaseOrg the sole persistence source for organization reads and writes while preserving legacy /api/org behavior through conversion utilities.

Normal application flows must no longer query, create, update, or delete records in the legacy Org collection.

Background

BaseOrgRepository currently has legacy-format options, but those options still read from and write to the old Org collection.

This creates dual-write complexity in organization creation, updates, full updates, and deletion. It also makes the old collection an active dependency rather than simply a deprecated representation.

Proposed approach

Do not create another repository.

Create a small, pure compatibility utility:

src/utils/orgCompatibility.js

It should contain focused conversion functions:

  • toLegacyOrg(baseOrg)
  • toBaseOrgInput(legacyOrg)
  • toBaseOrgUpdate(legacyParameters)

The utility should map these fields:

BaseOrg field Legacy Org field
long_name name
authority authority.active_roles
id_quota policies.id_quota
created time.created
last_updated time.modified

Do not convert, map, or migrate legacy inUse to BaseOrg in_use. They are distinct fields with potentially different semantics.

Refactor BaseOrgRepository so it always queries and writes BaseOrg.

Legacy-format options must control only input/output conversion:

  • Convert legacy input to BaseOrg input before persistence.
  • Convert BaseOrg results to legacy format immediately before returning a legacy response.
  • Never query or write the Org collection because a caller requested legacy format.

Required work

  • Add unit-tested conversion utilities for BaseOrg-to-legacy and legacy-to-BaseOrg transformations.
  • Refactor BaseOrgRepository to remove dependencies on OrgRepository.
  • Update these methods to persist only BaseOrg data:
    • createOrg
    • updateOrg
    • updateOrgFull
    • deleteOrg
    • legacy-format lookup and list methods
  • Remove dual-write and dual-read logic from those methods.
  • Preserve current BaseOrg responsibilities:
    • discriminator and authority changes
    • review-object processing
    • audit history
    • alias collision checks
    • organization hierarchy
    • user/admin membership
    • transaction and session propagation
  • Update direct getOrgRepository() consumers to use getBaseOrgRepository() and BaseOrg field names:
    • CVE controller
    • CVE-ID controller
    • middleware
  • Investigate and explicitly decide how the legacy CVE-ID reservation lock will work after removal of the legacy Org collection.
  • Preserve atomic reservation-lock semantics using a dedicated BaseOrg-safe lock field or mechanism, only after confirming the intended data model.
  • Remove runtime production dependencies on:
    • src/model/org.js
    • src/repositories/orgRepository.js

Simplification target

After this work, each write should have one persistence path:

const baseOrgInput = isLegacyObject
  ? toBaseOrgInput(incomingOrg)
  : incomingOrg

const savedOrg = await saveBaseOrg(baseOrgInput, options)

return isLegacyObject
  ? toLegacyOrg(savedOrg.toObject())
  : toRegistryResponse(savedOrg.toObject())

The conversion utility is an API compatibility boundary only. It must not synchronize or persist a second organization representation.

Data migration and rollout

  • Verify every legacy Org record has a matching BaseOrg record by UUID and short name.
  • Produce a mismatch report for missing records, duplicate UUIDs, roles, quotas, and mapped fields.
  • Resolve mismatches before enabling BaseOrg-only reads.
  • Add temporary telemetry or assertions proving normal request traffic no longer reads or writes Org.

Acceptance criteria

  • All /api/org reads, creates, updates, and deletes use BaseOrg only.
  • Legacy API responses retain:
    • name
    • authority.active_roles
    • policies.id_quota
    • time.created
    • time.modified
  • Registry API responses and schemas remain unchanged.
  • Organization creation, update, full update, and deletion no longer write to Org.
  • CVE and CVE-ID authorization, quota calculation, reassignment, and reservation locking work with BaseOrg-only data.
  • Reservation locking remains atomic and has an explicitly approved BaseOrg-compatible implementation.
  • Integration tests cover:
    • legacy /api/org CRUD with only BaseOrg seeded
    • legacy response field conversion
    • CVE-ID reservation locking
    • role-based authorization
    • no Org collection mutation during legacy-route operations
  • Existing integration suite passes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Needs Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions