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 }