github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/remote/registry/profiles.go (about) 1 package registry 2 3 import ( 4 "fmt" 5 "sort" 6 "sync" 7 "time" 8 ) 9 10 var ( 11 // nowFunc is an ps function for getting timestamps 12 nowFunc = func() time.Time { return time.Now() } 13 ) 14 15 // Profiles is the interface for working with a set of *Profile's 16 // Register, Deregister, Load, Len, Range, and SortedRange should be 17 // considered safe to hook up to public http endpoints, whereas 18 // Delete & Store should only be exposed in administrative contexts 19 // users should prefer using RegisterProfile & DegristerProfile for 20 // dataset manipulation operations 21 type Profiles interface { 22 // Len returns the number of records in the set 23 Len() (int, error) 24 // Load fetches a profile from the list by key 25 Load(key string) (value *Profile, err error) 26 // Range calls an iteration fuction on each element in the map until 27 // the end of the list is reached or iter returns true 28 Range(iter func(key string, p *Profile) (kontinue bool, err error)) error 29 // SortedRange is like range but with deterministic key ordering 30 SortedRange(iter func(key string, p *Profile) (kontinue bool, err error)) error 31 32 // Create adds an entry, bypassing the register process 33 // store is only exported for administrative use cases. 34 // most of the time callers should use Register instead 35 Create(key string, value *Profile) error 36 // Update modifies an existing profile 37 Update(key string, value *Profile) error 38 // Delete removes a profile from the set at key 39 // Delete is only exported for administrative use cases. 40 // most of the time callers should use Deregister instead 41 Delete(key string) error 42 } 43 44 // RegisterProfile adds a profile to the list if it's valid and the desired handle isn't taken 45 func RegisterProfile(store Profiles, p *Profile) (err error) { 46 if err = p.Validate(); err != nil { 47 return err 48 } 49 if err = p.Verify(); err != nil { 50 return err 51 } 52 53 pro, err := store.Load(p.Username) 54 if err == nil { 55 // if peer is registring a name they already own, we're good 56 if pro.ProfileID == p.ProfileID { 57 return nil 58 } 59 return fmt.Errorf("username '%s' is taken", p.Username) 60 } 61 62 prev := "" 63 store.Range(func(key string, profile *Profile) (bool, error) { 64 if profile.ProfileID == p.ProfileID { 65 prev = key 66 return true, nil 67 } 68 return false, nil 69 }) 70 71 if prev != "" { 72 if err = store.Delete(prev); err != nil { 73 return err 74 } 75 } 76 77 p.Created = time.Now() 78 return store.Create(p.Username, p) 79 } 80 81 // UpdateProfile alters profile data 82 func UpdateProfile(store Profiles, p *Profile) (err error) { 83 if err = p.Validate(); err != nil { 84 return err 85 } 86 if err = p.Verify(); err != nil { 87 return err 88 } 89 90 return store.Update(p.Username, p) 91 } 92 93 // DeregisterProfile removes a profile from the registry if it exists 94 // confirming the user has the authority to do so 95 func DeregisterProfile(store Profiles, p *Profile) error { 96 if err := p.Validate(); err != nil { 97 return err 98 } 99 if err := p.Verify(); err != nil { 100 return err 101 } 102 103 store.Delete(p.Username) 104 return nil 105 } 106 107 // MemProfiles is a map of profile data safe for concurrent use 108 // heavily inspired by sync.Map 109 type MemProfiles struct { 110 sync.RWMutex 111 ps map[string]*Profile 112 } 113 114 var _ Profiles = (*MemProfiles)(nil) 115 116 // NewMemProfiles allocates a new *MemProfiles map 117 func NewMemProfiles() *MemProfiles { 118 return &MemProfiles{ 119 ps: make(map[string]*Profile), 120 } 121 } 122 123 // Len returns the number of records in the map 124 func (ps *MemProfiles) Len() (int, error) { 125 return len(ps.ps), nil 126 } 127 128 // Load fetches a profile from the list by key 129 func (ps *MemProfiles) Load(key string) (value *Profile, err error) { 130 ps.RLock() 131 result, ok := ps.ps[key] 132 ps.RUnlock() 133 134 if !ok { 135 return nil, ErrNotFound 136 } 137 138 return result, nil 139 } 140 141 // Range calls an iteration fuction on each element in the map until 142 // the end of the list is reached or iter returns true 143 func (ps *MemProfiles) Range(iter func(key string, p *Profile) (kontinue bool, err error)) (err error) { 144 ps.RLock() 145 defer ps.RUnlock() 146 var kontinue bool 147 148 for key, p := range ps.ps { 149 kontinue, err = iter(key, p) 150 if err != nil { 151 return err 152 } 153 if !kontinue { 154 break 155 } 156 } 157 158 return nil 159 } 160 161 // SortedRange is like range but with deterministic key ordering 162 func (ps *MemProfiles) SortedRange(iter func(key string, p *Profile) (kontinue bool, err error)) (err error) { 163 ps.RLock() 164 defer ps.RUnlock() 165 keys := make([]string, len(ps.ps)) 166 i := 0 167 for key := range ps.ps { 168 keys[i] = key 169 i++ 170 } 171 sort.StringSlice(keys).Sort() 172 173 var kontinue bool 174 for _, key := range keys { 175 kontinue, err = iter(key, ps.ps[key]) 176 if err != nil { 177 return err 178 } 179 if !kontinue { 180 break 181 } 182 } 183 return nil 184 } 185 186 // Delete removes a record from MemProfiles at key 187 func (ps *MemProfiles) Delete(key string) error { 188 ps.Lock() 189 delete(ps.ps, key) 190 ps.Unlock() 191 return nil 192 } 193 194 // Create adds a profile 195 func (ps *MemProfiles) Create(key string, value *Profile) error { 196 ps.Lock() 197 ps.ps[key] = value 198 ps.Unlock() 199 return nil 200 } 201 202 // Update modifies an existing profile 203 func (ps *MemProfiles) Update(key string, value *Profile) error { 204 ps.Lock() 205 ps.ps[key] = value 206 ps.Unlock() 207 return nil 208 }