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  }