feat: CheckSnapshots y UpdateSnapshot en fn_operations library

Añade UpdateTypeSnapshot al store, CheckSnapshots para comparar snapshots
locales vs registry (up_to_date/outdated/missing), y UpdateSnapshot para
re-snapshot con retorno de old/new para diffing.
This commit is contained in:
2026-03-28 13:41:35 +01:00
parent 762fcbeb58
commit 0095de2ce7
2 changed files with 102 additions and 0 deletions
+88
View File
@@ -189,6 +189,94 @@ func GetEntityGraph(db *DB) (*Graph, error) {
}, nil
}
// SnapshotStatus describes the state of a snapshot vs the registry.
type SnapshotStatus string
const (
SnapshotUpToDate SnapshotStatus = "up_to_date"
SnapshotOutdated SnapshotStatus = "outdated"
SnapshotMissing SnapshotStatus = "missing" // exists locally but not in registry
)
// SnapshotCheckResult holds the comparison for one type snapshot.
type SnapshotCheckResult struct {
ID string
LocalVersion string
RegistryVersion string
Status SnapshotStatus
}
// CheckSnapshots compares all local snapshots against the registry.
func CheckSnapshots(opsDB *DB, registryDB *registry.DB) ([]SnapshotCheckResult, error) {
snaps, err := opsDB.ListTypeSnapshots()
if err != nil {
return nil, fmt.Errorf("listing snapshots: %w", err)
}
var results []SnapshotCheckResult
for _, snap := range snaps {
regType, err := registryDB.GetType(snap.ID)
if err != nil {
// Not found in registry
results = append(results, SnapshotCheckResult{
ID: snap.ID,
LocalVersion: snap.Version,
Status: SnapshotMissing,
})
continue
}
status := SnapshotUpToDate
if regType.Version != snap.Version {
status = SnapshotOutdated
}
results = append(results, SnapshotCheckResult{
ID: snap.ID,
LocalVersion: snap.Version,
RegistryVersion: regType.Version,
Status: status,
})
}
return results, nil
}
// UpdateSnapshot re-snapshots a type from the registry, replacing the local copy.
// Returns the old and new definitions for diffing.
func UpdateSnapshot(opsDB *DB, registryDB *registry.DB, typeID string) (old, new_ *TypeSnapshot, err error) {
// Get current local snapshot
oldSnap, err := opsDB.GetTypeSnapshot(typeID)
if err != nil {
return nil, nil, fmt.Errorf("reading local snapshot: %w", err)
}
if oldSnap == nil {
return nil, nil, fmt.Errorf("type %q not found in local snapshots", typeID)
}
// Get current registry type
regType, err := registryDB.GetType(typeID)
if err != nil {
return nil, nil, fmt.Errorf("fetching type %q from registry: %w", typeID, err)
}
// Build new snapshot
newSnap := &TypeSnapshot{
ID: regType.ID,
Version: regType.Version,
Lang: regType.Lang,
Algebraic: string(regType.Algebraic),
Definition: regType.Definition,
Description: regType.Description,
SnappedAt: time.Now().UTC(),
}
if err := opsDB.UpdateTypeSnapshot(newSnap); err != nil {
return nil, nil, fmt.Errorf("updating snapshot: %w", err)
}
return oldSnap, newSnap, nil
}
func buildEntitySet(db *DB) (map[string]bool, error) {
all, err := db.ListEntities("", "")
if err != nil {
+14
View File
@@ -58,6 +58,20 @@ func (db *DB) InsertTypeSnapshot(ts *TypeSnapshot) error {
return err
}
// UpdateTypeSnapshot replaces an existing snapshot with a new version.
func (db *DB) UpdateTypeSnapshot(ts *TypeSnapshot) error {
if ts.SnappedAt.IsZero() {
ts.SnappedAt = time.Now().UTC()
}
_, err := db.conn.Exec(`
UPDATE types_snapshot SET version=?, lang=?, algebraic=?, definition=?, description=?, snapped_at=?
WHERE id=?`,
ts.Version, ts.Lang, ts.Algebraic, ts.Definition, ts.Description,
ts.SnappedAt.Format(time.RFC3339), ts.ID,
)
return err
}
// GetTypeSnapshot returns a type snapshot by ID.
func (db *DB) GetTypeSnapshot(id string) (*TypeSnapshot, error) {
row := db.conn.QueryRow("SELECT id, version, lang, algebraic, definition, description, snapped_at FROM types_snapshot WHERE id = ?", id)