github.com/best4tires/kit@v1.0.5/srv/meta.go (about) 1 package srv 2 3 import ( 4 "fmt" 5 "net/http" 6 "strconv" 7 "strings" 8 9 "github.com/best4tires/kit/convert" 10 ) 11 12 // Meta holds various filter and query parameters passed with a http request 13 type Meta struct { 14 Limit int 15 Skip int 16 Filters []Filter 17 Sorts SortComponents 18 } 19 20 type SortOrder string 21 22 const ( 23 SortNone SortOrder = "None" 24 SortASC SortOrder = "ASC" 25 SortDESC SortOrder = "DESC" 26 ) 27 28 func (o SortOrder) Valid() bool { 29 return o == SortASC || o == SortDESC || o == SortNone 30 } 31 32 func (o SortOrder) String() string { 33 return string(o) 34 } 35 36 func (o SortOrder) IfLess(less bool) bool { 37 switch o { 38 case SortASC: 39 return less 40 case SortDESC: 41 return !less 42 default: 43 return less 44 } 45 } 46 47 type SortField struct { 48 Name string `json:"name"` 49 Label string `json:"label"` 50 } 51 52 type SortConfiguration struct { 53 Fields []SortField `json:"fields,omitempty"` 54 Components SortComponents `json:"components,omitempty"` 55 } 56 57 type SortComponent struct { 58 Name string `json:"name"` 59 Order SortOrder `json:"order"` 60 } 61 62 func (sc SortComponent) String() string { 63 return sc.Name + ":" + sc.Order.String() 64 } 65 66 type SortComponents []SortComponent 67 68 func (scs SortComponents) String() string { 69 var sl []string 70 for _, sc := range scs { 71 sl = append(sl, sc.String()) 72 } 73 return strings.Join(sl, "; ") 74 } 75 76 func (m Meta) SortOrder(name string) SortOrder { 77 for _, sc := range m.Sorts { 78 if sc.Name == name { 79 return sc.Order 80 } 81 } 82 return SortNone 83 } 84 85 func (m Meta) FilterValue(name string) interface{} { 86 for _, f := range m.Filters { 87 if f.Name == name { 88 return f.Value 89 } 90 } 91 return nil 92 } 93 94 func (m Meta) FilterValueBool(name string) bool { 95 for _, f := range m.Filters { 96 if f.Name == name { 97 return convert.ToBool(f.Value) 98 } 99 } 100 return false 101 } 102 103 func (m Meta) FilterValueString(name string) string { 104 for _, f := range m.Filters { 105 if f.Name == name { 106 return fmt.Sprintf("%v", f.Value) 107 } 108 } 109 return "" 110 } 111 112 func (m Meta) FilterValueInt(name string) int { 113 for _, f := range m.Filters { 114 if f.Name == name { 115 v, ok := convert.ToInt(f.Value) 116 if !ok { 117 return 0 118 } else { 119 return v 120 } 121 } 122 } 123 return 0 124 } 125 126 func (m *Meta) ChangeFilterValueInt(name string, v int) { 127 for i, f := range m.Filters { 128 if f.Name == name { 129 m.Filters[i].Value = fmt.Sprintf("%d", v) 130 return 131 } 132 } 133 m.Filters = append(m.Filters, Filter{ 134 Name: name, 135 Comparator: FilterComparatorEqual, 136 Value: fmt.Sprintf("%d", v), 137 }) 138 } 139 140 func (m Meta) FilterValueFloat(name string) float64 { 141 for _, f := range m.Filters { 142 if f.Name == name { 143 v, ok := convert.ToFloat(f.Value) 144 if !ok { 145 return 0 146 } else { 147 return v 148 } 149 } 150 } 151 return 0 152 } 153 154 // FilterComparator types a comparator 155 type FilterComparator string 156 157 const ( 158 FilterComparatorEqual FilterComparator = "eq" 159 FilterComparatorLess FilterComparator = "ls" 160 FilterComparatorGreater FilterComparator = "gt" 161 FilterComparatorLike FilterComparator = "like" 162 ) 163 164 func parseFilterComparator(c string) (FilterComparator, error) { 165 switch FilterComparator(c) { 166 case FilterComparatorEqual: 167 return FilterComparatorEqual, nil 168 case FilterComparatorLess: 169 return FilterComparatorLess, nil 170 case FilterComparatorGreater: 171 return FilterComparatorGreater, nil 172 case FilterComparatorLike: 173 return FilterComparatorLike, nil 174 default: 175 return "", fmt.Errorf("invalid comparator") 176 } 177 } 178 179 // Filter defines a general filter 180 type Filter struct { 181 Name string 182 Comparator FilterComparator 183 Value string 184 } 185 186 func (f Filter) query() string { 187 return fmt.Sprintf("filter=%s,%s,%s", f.Name, f.Comparator, f.Value) 188 } 189 190 func parseFilter(s string) (Filter, error) { 191 sl := strings.Split(s, ",") 192 if len(sl) != 3 { 193 return Filter{}, fmt.Errorf("invalid filter format") 194 } 195 c, err := parseFilterComparator(sl[1]) 196 if err != nil { 197 return Filter{}, err 198 } 199 return Filter{ 200 Name: sl[0], 201 Comparator: c, 202 Value: sl[2], 203 }, nil 204 } 205 206 func (scs SortComponents) query() string { 207 var sl []string 208 for _, sc := range scs { 209 sl = append(sl, fmt.Sprintf("%s:%s", sc.Name, sc.Order)) 210 } 211 if len(sl) == 0 { 212 return "" 213 } 214 return fmt.Sprintf("sort=%s", strings.Join(sl, ",")) 215 } 216 217 // parseSort expects its values like "foo:asc,bar:desc ..." 218 func parseSort(s string) (SortComponents, error) { 219 sl := strings.Split(s, ",") 220 var cs SortComponents 221 for _, c := range sl { 222 csl := strings.Split(c, ":") 223 if len(csl) == 0 || len(csl) > 2 { 224 return SortComponents{}, fmt.Errorf("invalid sort component format %q", c) 225 } 226 name := csl[0] 227 order := SortASC 228 if len(csl) == 2 { 229 order = SortOrder(csl[1]) 230 } 231 cs = append(cs, SortComponent{ 232 Name: name, 233 Order: order, 234 }) 235 } 236 return cs, nil 237 } 238 239 func extractNumber(vs []string) (int, error) { 240 if len(vs) == 0 { 241 return 0, fmt.Errorf("no values") 242 } 243 n, err := strconv.ParseInt(vs[0], 10, 64) 244 if err != nil { 245 return 0, err 246 } 247 return int(n), nil 248 } 249 250 // ParseMeta parses meta-data from a http request 251 func ParseMeta(r *http.Request) (Meta, error) { 252 m := Meta{} 253 var err error 254 for key, values := range r.URL.Query() { 255 switch key { 256 case "limit": 257 if m.Limit, err = extractNumber(values); err != nil { 258 return m, err 259 } 260 case "skip": 261 if m.Skip, err = extractNumber(values); err != nil { 262 return m, err 263 } 264 case "filter": 265 for _, s := range values { 266 f, err := parseFilter(s) 267 if err != nil { 268 return m, err 269 } 270 m.Filters = append(m.Filters, f) 271 } 272 case "sort": 273 if len(values) > 0 { 274 m.Sorts, err = parseSort(values[0]) 275 if err != nil { 276 return m, err 277 } 278 } 279 } 280 } 281 return m, nil 282 } 283 284 func (m Meta) Query() string { 285 var elts []string 286 elts = append(elts, fmt.Sprintf("limit=%d", m.Limit)) 287 elts = append(elts, fmt.Sprintf("skip=%d", m.Skip)) 288 for _, f := range m.Filters { 289 elts = append(elts, f.query()) 290 } 291 sq := m.Sorts.query() 292 if sq != "" { 293 elts = append(elts, sq) 294 } 295 return strings.Join(elts, "&") 296 }