Files
at-container-registry/pkg/testpds/records.go
T

111 lines
3.0 KiB
Go

package testpds
import (
"crypto/sha256"
"encoding/base32"
"encoding/json"
"sync"
)
// recordStore is the in-memory backing for the fake PDS's repo.* endpoints.
// Records are keyed by (did, collection, rkey). Values are stored as raw JSON
// so the fake never has to know the lexicon shape.
type recordStore struct {
mu sync.RWMutex
records map[string]map[string]map[string]json.RawMessage
}
func newRecordStore() *recordStore {
return &recordStore{records: make(map[string]map[string]map[string]json.RawMessage)}
}
func (s *recordStore) put(did, collection, rkey string, value json.RawMessage) {
s.mu.Lock()
defer s.mu.Unlock()
if s.records[did] == nil {
s.records[did] = make(map[string]map[string]json.RawMessage)
}
if s.records[did][collection] == nil {
s.records[did][collection] = make(map[string]json.RawMessage)
}
s.records[did][collection][rkey] = value
}
func (s *recordStore) get(did, collection, rkey string) (json.RawMessage, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
if s.records[did] == nil || s.records[did][collection] == nil {
return nil, false
}
v, ok := s.records[did][collection][rkey]
return v, ok
}
func (s *recordStore) delete(did, collection, rkey string) bool {
s.mu.Lock()
defer s.mu.Unlock()
if s.records[did] == nil || s.records[did][collection] == nil {
return false
}
if _, ok := s.records[did][collection][rkey]; !ok {
return false
}
delete(s.records[did][collection], rkey)
return true
}
// list returns a snapshot of all (rkey, value) pairs in the named collection,
// in undefined order. Callers are expected to sort if they care.
func (s *recordStore) list(did, collection string) []recordEntry {
s.mu.RLock()
defer s.mu.RUnlock()
if s.records[did] == nil || s.records[did][collection] == nil {
return nil
}
out := make([]recordEntry, 0, len(s.records[did][collection]))
for rkey, v := range s.records[did][collection] {
out = append(out, recordEntry{RKey: rkey, Value: v})
}
return out
}
type recordEntry struct {
RKey string
Value json.RawMessage
}
// fakeCID synthesizes a deterministic-looking CID for a record. Production
// code rarely validates this against the actual record content; it just
// echoes it back. We use a SHA-256 of the value, base32-encoded with a CIDv1
// prefix-like marker.
var b32 = base32.StdEncoding.WithPadding(base32.NoPadding)
func fakeCID(value []byte) string {
h := sha256.Sum256(value)
return "bafy" + b32.EncodeToString(h[:24])
}
// blobStore is a simple content-addressed store for ATProto blobs. The
// uploadBlob handler writes bytes here; sync.getBlob reads them back by CID.
type blobStore struct {
mu sync.RWMutex
blobs map[string][]byte
}
func newBlobStore() *blobStore {
return &blobStore{blobs: make(map[string][]byte)}
}
func (b *blobStore) put(cid string, data []byte) {
b.mu.Lock()
defer b.mu.Unlock()
b.blobs[cid] = data
}
func (b *blobStore) get(cid string) ([]byte, bool) {
b.mu.RLock()
defer b.mu.RUnlock()
v, ok := b.blobs[cid]
return v, ok
}