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  }