proposedchanges

package
v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 7 Imported by: 0

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

View Source
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.

View Source
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

func CountPendingBySubmitter(ctx context.Context, submittedBy string) (int, error)

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

func EnsureIndexes(ctx context.Context) error

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.

func Update

func Update(ctx context.Context, p *ProposedChange) error

Update persists changes to an existing proposed change (its Diff field statuses, derived Status, and review metadata).

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL