Mechanism-independent groundwork for the write-path migration. Mirrors the slice-B ItemReader pattern: - store/adapter.go: ItemWriter interface extracted from *Store's write surface (Create/Update/Reparent/AddParent/SetPublic/SetPinned/ SoftDelete/SoftDeleteCascade + AddLink/AddLinkDated/DeleteLink), with a compile-time witness that *Store satisfies it. - store/mbrian_writer.go: MBrianWriter stub — an HTTP client (NOT a pgx writer) against mBrian's scoped /api/projax/* write API per head's mechanism call (option c). Bodies return errNotImplementedSliceC until the HTTP impl + final spec land. Holds a pool for write-path read-backs. - web/server.go: Server.Writes field (twin of Items), defaulted to the concrete *Store in web.New. main.go will flip Items+Writes atomically — the slice-B half-flip (reads mBrian, writes projax.items) was the bug. No behaviour change: both Items and Writes still resolve to *Store.
95 lines
4.5 KiB
Go
95 lines
4.5 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// ItemReader is the read-path contract every projax UI handler, the
|
|
// internal/aggregate fan-out engine, and the MCP read tools depend on.
|
|
// Pure projax-shaped structs in/out; the slice-B mBrian-backed
|
|
// implementation translates mBrian nodes/edges into the same shape
|
|
// without leaking mBrian types to consumers.
|
|
//
|
|
// Phase 6 Slice B (live impl) — see store/mbrian.go for the MBrianReader
|
|
// implementation against the migrated mbrian.* schema, and
|
|
// docs/plans/slice-b-adapter-contract.md for the consumer inventory +
|
|
// per-method semantics.
|
|
//
|
|
// Two satisfiers ship:
|
|
// *Store — pgx-backed against projax.items (today; legacy).
|
|
// *MBrianReader — pgx-backed against mbrian.{nodes,edges} (slice B).
|
|
//
|
|
// Selection between them is wired at Server-construction time via
|
|
// PROJAX_BACKEND=store|mbrian (defaults to "store" until slice B is
|
|
// rolled to production).
|
|
type ItemReader interface {
|
|
// --- item lookups ---
|
|
ListAll(ctx context.Context) ([]*Item, error)
|
|
GetByID(ctx context.Context, id string) (*Item, error)
|
|
GetByPath(ctx context.Context, path string) (*Item, error)
|
|
GetByPathOrSlug(ctx context.Context, key string) (*Item, error)
|
|
Roots(ctx context.Context) ([]*Item, error)
|
|
MaiOrphans(ctx context.Context) ([]*Item, error)
|
|
ListByFilters(ctx context.Context, f SearchFilters) ([]*Item, error)
|
|
Search(ctx context.Context, q string, limit int) ([]*Item, error)
|
|
ItemsCreatedInRange(ctx context.Context, from, to time.Time) ([]*Item, error)
|
|
AllTags(ctx context.Context) ([]string, error)
|
|
|
|
// --- link lookups ---
|
|
LinksByType(ctx context.Context, itemID, refType string) ([]*ItemLink, error)
|
|
LinksByRefType(ctx context.Context, refType string) ([]*ItemLink, error)
|
|
DatedLinks(ctx context.Context, itemID string) ([]*ItemLink, error)
|
|
DatedLinksRange(ctx context.Context, from, to time.Time) ([]*ItemLinkWithItem, error)
|
|
RecentDocuments(ctx context.Context, since time.Time, limit int) ([]*ItemLinkWithItem, error)
|
|
}
|
|
|
|
// Compile-time assertion that the existing pgx-backed *Store satisfies
|
|
// ItemReader. Drops in cleanly because every method in the interface is
|
|
// already part of *Store's public surface. If a future refactor removes
|
|
// or reshapes one of these methods on *Store, the compiler points at
|
|
// this line first.
|
|
var _ ItemReader = (*Store)(nil)
|
|
|
|
// ItemWriter is the write-path contract every projax UI write handler, the
|
|
// /admin/bulk apply path, and the MCP write tools depend on. Pure
|
|
// projax-shaped structs in/out; the slice-C mBrian-backed implementation
|
|
// (*MBrianWriter) translates each call into the scoped mBrian HTTP write
|
|
// API without leaking mBrian/HTTP types to consumers.
|
|
//
|
|
// Phase 6 Slice C — see store/mbrian_writer.go for the MBrianWriter
|
|
// implementation (an HTTP client against mBrian's /api/projax/* surface)
|
|
// and docs/plans/slice-c-writepath-contract.md for the per-method
|
|
// semantics + the read→write→read round-trip the slice-B cutover missed.
|
|
//
|
|
// Two satisfiers ship:
|
|
// *Store — pgx-backed against projax.items (today; legacy).
|
|
// *MBrianWriter — HTTP client against mBrian's scoped write API (slice C).
|
|
//
|
|
// Selection between them is wired at Server-construction time via
|
|
// PROJAX_BACKEND=store|mbrian. The flag MUST flip BOTH the reader
|
|
// (Server.Items) and the writer (Server.Writes) to the same backend —
|
|
// the slice-B bug was a half-flip (reads on mBrian, writes on
|
|
// projax.items), so a read-then-write round-trip rejected the freshly
|
|
// read id. main.go sets both atomically.
|
|
type ItemWriter interface {
|
|
// --- item writes ---
|
|
Create(ctx context.Context, in CreateInput) (*Item, error)
|
|
Update(ctx context.Context, id string, in UpdateInput) (*Item, error)
|
|
Reparent(ctx context.Context, id string, parentIDs []string) (*Item, error)
|
|
AddParent(ctx context.Context, id, parentID string) (*Item, error)
|
|
SetPublic(ctx context.Context, ids []string, public bool) error
|
|
SetPinned(ctx context.Context, ids []string, pinned bool) error
|
|
SoftDelete(ctx context.Context, id string) error
|
|
SoftDeleteCascade(ctx context.Context, id string, cascade bool) error
|
|
|
|
// --- link writes ---
|
|
AddLink(ctx context.Context, itemID, refType, refID, rel string, metadata map[string]any) (*ItemLink, error)
|
|
AddLinkDated(ctx context.Context, itemID, refType, refID, rel string, note *string, eventDate *time.Time, metadata map[string]any) (*ItemLink, error)
|
|
DeleteLink(ctx context.Context, id string) error
|
|
}
|
|
|
|
// Compile-time assertion that the existing pgx-backed *Store satisfies
|
|
// ItemWriter. Every method is already part of *Store's public surface.
|
|
var _ ItemWriter = (*Store)(nil)
|