Documentation
¶
Overview ¶
Package proposedchanges stores field-level proposed edits to a record (a volume today, other record types later - see design.md's "generic proposed-change shape" decision) separately from the live record until an editor/admin reviews them. See platform's volume-proposed-changes spec (openspec/changes/volume-edit-with-approval-workflow in sweetrpg/platform).
Index ¶
Constants ¶
const ( StatusPending = "pending" StatusAccepted = "accepted" StatusRejected = "rejected" StatusPartiallyAccepted = "partially_accepted" StatusRetracted = "retracted" )
Field-level and overall proposal statuses.
StatusRetracted is submitter-initiated (the submitter withdraws their own pending proposal, see task 5.3) and distinct from StatusRejected (a reviewer's outcome) - both are terminal and non-pending, but who initiated it and why differ, which matters for reporting/audit.
const CollectionName = "proposed_changes"
Variables ¶
This section is empty.
Functions ¶
func Add ¶
func Add(ctx context.Context, p *ProposedChange) (string, error)
Add stores a new pending proposed change, stamping SubmittedAt, and returns its ID.
func CountPendingBySubmitter ¶ added in v0.11.0
CountPendingBySubmitter counts submittedBy's pending proposals across every record - the unapproved-submission-cap check at finalize time (task 5.1) is per-user, not per-record.
func EnsureIndexes ¶
EnsureIndexes creates the compound index this package's queries rely on (record_type + record_id + status, for the per-record pending-list lookup). Safe to call on every startup - CreateOne is a no-op if an equivalent index already exists.
Types ¶
type FieldChange ¶
type FieldChange struct {
Old any `bson:"old" json:"old"`
New any `bson:"new" json:"new"`
Status string `bson:"status" json:"status"`
}
FieldChange is one changed field's old (live-at-submission-time) and proposed value, plus its own review outcome once decided.
type ProposedChange ¶
type ProposedChange struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
RecordType string `bson:"record_type" json:"recordType"`
RecordID string `bson:"record_id" json:"recordId"`
Diff map[string]FieldChange `bson:"diff" json:"diff"`
Status string `bson:"status" json:"status"`
SubmittedBy string `bson:"submitted_by" json:"submittedBy"`
SubmittedAt time.Time `bson:"submitted_at" json:"submittedAt"`
ReviewedBy string `bson:"reviewed_by,omitempty" json:"reviewedBy,omitempty"`
ReviewedAt *time.Time `bson:"reviewed_at,omitempty" json:"reviewedAt,omitempty"`
ReviewNote string `bson:"review_note,omitempty" json:"reviewNote,omitempty"`
StagedCoverAssetId string `bson:"staged_cover_asset_id,omitempty" json:"stagedCoverAssetId,omitempty"`
StagedSampleAssetIds []string `bson:"staged_sample_asset_ids,omitempty" json:"stagedSampleAssetIds,omitempty"`
}
ProposedChange is a submitter's proposed edit to a live record, pending admin/editor review.
StagedCoverAssetId/StagedSampleAssetIds are separate from Diff (not settable as a normal field change) - they reference assets-web's staged asset store (see the durable-volume-editing change's volume-cover-staging/volume-sample-pages specs). Accepting the proposal promotes them to live (assets.Client.Promote) and applies the resulting live ids to the volume; rejecting reclaims (deletes) them without promoting. Empty/nil means the proposal didn't reference a staged cover or samples.
func Get ¶
func Get(ctx context.Context, id string) (*ProposedChange, error)
Get fetches a single proposed change by ID.
func ListPending ¶
func ListPending(ctx context.Context, recordType, recordID string) ([]*ProposedChange, error)
ListPending returns every pending proposed change for the given record, oldest first.
func ListPendingByType ¶ added in v0.12.0
func ListPendingByType(ctx context.Context, recordType string) ([]*ProposedChange, error)
ListPendingByType returns every pending proposed change across all records of one type, oldest first - used by the version-model migration (cmd/migrate-volumes) to find every still-pending proposal that needs to become a submitted version, not just one record's.
func (*ProposedChange) DeriveStatus ¶
func (p *ProposedChange) DeriveStatus()
DeriveStatus recomputes Status from each field's individual outcome: pending while any field is undecided, accepted/rejected if every decided field agrees, partially_accepted on a mix. Called after any review action so the top-level status never has to be set independently of the fields it's derived from.