github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/plugin/helpers_kv.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package plugin
     5  
     6  import (
     7  	"encoding/json"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // KVSetJSON implements Helpers.KVSetJSON.
    14  func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error {
    15  	err := p.ensureServerVersion("5.2.0")
    16  	if err != nil {
    17  		return err
    18  	}
    19  
    20  	data, err := json.Marshal(value)
    21  	if err != nil {
    22  		return err
    23  	}
    24  
    25  	appErr := p.API.KVSet(key, data)
    26  	if appErr != nil {
    27  		return appErr
    28  	}
    29  
    30  	return nil
    31  }
    32  
    33  // KVCompareAndSetJSON implements Helpers.KVCompareAndSetJSON.
    34  func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) {
    35  	var err error
    36  
    37  	err = p.ensureServerVersion("5.12.0")
    38  	if err != nil {
    39  		return false, err
    40  	}
    41  	var oldData, newData []byte
    42  
    43  	if oldValue != nil {
    44  		oldData, err = json.Marshal(oldValue)
    45  		if err != nil {
    46  			return false, errors.Wrap(err, "unable to marshal old value")
    47  		}
    48  	}
    49  
    50  	if newValue != nil {
    51  		newData, err = json.Marshal(newValue)
    52  		if err != nil {
    53  			return false, errors.Wrap(err, "unable to marshal new value")
    54  		}
    55  	}
    56  
    57  	set, appErr := p.API.KVCompareAndSet(key, oldData, newData)
    58  	if appErr != nil {
    59  		return set, appErr
    60  	}
    61  
    62  	return set, nil
    63  }
    64  
    65  // KVCompareAndDeleteJSON implements Helpers.KVCompareAndDeleteJSON.
    66  func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) {
    67  	var err error
    68  
    69  	err = p.ensureServerVersion("5.16.0")
    70  	if err != nil {
    71  		return false, err
    72  	}
    73  
    74  	var oldData []byte
    75  
    76  	if oldValue != nil {
    77  		oldData, err = json.Marshal(oldValue)
    78  		if err != nil {
    79  			return false, errors.Wrap(err, "unable to marshal old value")
    80  		}
    81  	}
    82  
    83  	deleted, appErr := p.API.KVCompareAndDelete(key, oldData)
    84  	if appErr != nil {
    85  		return deleted, appErr
    86  	}
    87  
    88  	return deleted, nil
    89  }
    90  
    91  // KVGetJSON implements Helpers.KVGetJSON.
    92  func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) {
    93  	err := p.ensureServerVersion("5.2.0")
    94  	if err != nil {
    95  		return false, err
    96  	}
    97  
    98  	data, appErr := p.API.KVGet(key)
    99  	if appErr != nil {
   100  		return false, appErr
   101  	}
   102  	if data == nil {
   103  		return false, nil
   104  	}
   105  
   106  	err = json.Unmarshal(data, value)
   107  	if err != nil {
   108  		return false, err
   109  	}
   110  
   111  	return true, nil
   112  }
   113  
   114  // KVSetWithExpiryJSON is a wrapper around KVSetWithExpiry to simplify atomically writing a JSON object with expiry to the key value store.
   115  func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error {
   116  	err := p.ensureServerVersion("5.6.0")
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	data, err := json.Marshal(value)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	appErr := p.API.KVSetWithExpiry(key, data, expireInSeconds)
   127  	if appErr != nil {
   128  		return appErr
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  type kvListOptions struct {
   135  	checkers []func(key string) (keep bool, err error)
   136  }
   137  
   138  func (o *kvListOptions) checkAll(key string) (keep bool, err error) {
   139  	for _, check := range o.checkers {
   140  		keep, err := check(key)
   141  		if err != nil {
   142  			return false, err
   143  		}
   144  		if !keep {
   145  			return false, nil
   146  		}
   147  	}
   148  
   149  	// key made it through all checkers
   150  	return true, nil
   151  }
   152  
   153  // KVListOption represents a single input option for KVListWithOptions
   154  type KVListOption func(*kvListOptions)
   155  
   156  // WithPrefix only return keys that start with the given string.
   157  func WithPrefix(prefix string) KVListOption {
   158  	return WithChecker(func(key string) (keep bool, err error) {
   159  		return strings.HasPrefix(key, prefix), nil
   160  	})
   161  }
   162  
   163  // WithChecker allows for a custom filter function to determine which keys to return.
   164  // Returning true will keep the key and false will filter it out. Returning an error
   165  // will halt KVListWithOptions immediately and pass the error up (with no other results).
   166  func WithChecker(f func(key string) (keep bool, err error)) KVListOption {
   167  	return func(args *kvListOptions) {
   168  		args.checkers = append(args.checkers, f)
   169  	}
   170  }
   171  
   172  // kvListPerPage is the number of keys KVListWithOptions gets per request
   173  const kvListPerPage = 100
   174  
   175  // KVListWithOptions implements Helpers.KVListWithOptions.
   176  func (p *HelpersImpl) KVListWithOptions(options ...KVListOption) ([]string, error) {
   177  	err := p.ensureServerVersion("5.6.0")
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	// convert functional options into args struct
   182  	args := &kvListOptions{}
   183  	for _, opt := range options {
   184  		opt(args)
   185  	}
   186  	ret := make([]string, 0)
   187  
   188  	// get our keys a batch at a time, filter out the ones we don't want based on our args
   189  	// any errors will hault the whole process and return the error raw
   190  	for i := 0; ; i++ {
   191  		keys, appErr := p.API.KVList(i, kvListPerPage)
   192  		if appErr != nil {
   193  			return nil, appErr
   194  		}
   195  
   196  		if len(args.checkers) == 0 {
   197  			// no checkers, just append the whole block at once
   198  			ret = append(ret, keys...)
   199  		} else {
   200  			// we have a filter, so check each key, all checkers must say key
   201  			// for us to keep a key
   202  			for _, key := range keys {
   203  				keep, err := args.checkAll(key)
   204  				if err != nil {
   205  					return nil, err
   206  				}
   207  				if !keep {
   208  					continue
   209  				}
   210  
   211  				// didn't get filtered out, add to our return
   212  				ret = append(ret, key)
   213  			}
   214  		}
   215  
   216  		if len(keys) < kvListPerPage {
   217  			break
   218  		}
   219  	}
   220  
   221  	return ret, nil
   222  }