filtering.go 2.3 KB
package webutility

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

type Filter map[string][]string

func (f Filter) Get(key string) (values []string, ok bool) {
	values, ok = f[key]
	return values, ok
}

func (f Filter) Size() int {
	return len(f)
}

func (f Filter) IsNotEmpty() bool {
	return len(f) > 0
}

func (f Filter) ValueAt(val string, index int) string {
	if filter, ok := f[val]; ok {
		if len(filter) > index {
			return filter[index]
		}
	}

	return ""
}

func (fs Filter) validate(validFilters []string) (Filter, bool) {
	goodFilters := make(Filter)
	cnt, len := 0, 0
	for f, _ := range fs {
		len++
		for _, v := range validFilters {
			if f == v {
				cnt++
				goodFilters[f] = fs[f]
			}
		}
	}

	result := true
	if len > 0 && cnt == 0 {
		// if no valid filters are found declare filtering request as invalid
		result = false
	}

	return goodFilters, result
}

// requires input in format: "param1::value1|param2::value2..."
func ParseFilters(req *http.Request, header string) (filters Filter) {
	q := req.FormValue(header)
	q = strings.Trim(q, "\"")
	kvp := strings.Split(q, "|")
	filters = make(Filter, len(kvp))

	for i, _ := range kvp {
		kv := strings.Split(kvp[i], "::")
		if len(kv) == 2 {
			key, _ := url.QueryUnescape(kv[0])

			// get values (if more than 1)
			vals := strings.Split(kv[1], ",")
			for _, v := range vals {
				u, _ := url.QueryUnescape(v)
				filters[key] = append(filters[key], u)
			}
		}
	}

	return filters
}

// TODO(marko): very dodgy, needs more robustness
func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) {
	if prefix != "" {
		prefix += "."
	}

	if !filters.IsNotEmpty() {
		return "", true
	}

	filters, ok = filters.validate(validFilters)
	if !ok {
		return "", false
	}

	for k, filter := range filters {
		first := true
		symbol := "="

		if first {
			res += " and "
			first = false
		} else {
			res += " where "
		}

		res += "("
		for i, f := range filter {
			if strings.HasPrefix(f, "<") || strings.HasPrefix(f, ">") {
				symbol = string(f[0])
				f = strings.TrimLeft(f, "<>")
				if strings.HasPrefix(f, "=") {
					f = strings.TrimLeft(f, "=")
					symbol += "="
				}
			}

			res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, f)

			if i < len(filter)-1 {
				res += " or "
			}
		}
		res += ")"
	}

	return res, ok
}