new endpoint to return product list
This commit is contained in:
43
app/utils/query/query_params/key_mapping.go
Normal file
43
app/utils/query/query_params/key_mapping.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package query_params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
mreflect "git.ma-al.com/goc_daniel/b2b/app/utils/reflect"
|
||||
)
|
||||
|
||||
// MapParamsKeyToDbColumn will attempt to map provided key into unique (prefixed
|
||||
// with table) column name. It will do so using following priority of sources of
|
||||
// mapping:
|
||||
// 1. `formColumnMapping` argument. If the mapped values contain a dot, the part
|
||||
// before the dot will be used for the table name. Otherwise the table name will
|
||||
// be derived from the generic parameter `T`.
|
||||
// 2. json tags of provided as generic `T` struct. The table name will be also
|
||||
// derived from the generic if not provided as dot prefix.
|
||||
func MapParamsKeyToDbColumn[DEFAULT_TABLE_MODEL any](key string, mapping ...map[string]string) (string, error) {
|
||||
ERR := "Failed to find appropiate mapping from form field to database column for key: '%s', and default table name: '%s'"
|
||||
|
||||
if len(mapping) > 0 {
|
||||
if field, ok := (mapping[0])[key]; ok {
|
||||
return field, nil
|
||||
}
|
||||
} else {
|
||||
var t DEFAULT_TABLE_MODEL
|
||||
if table, field, ok := strings.Cut(key, "."); ok {
|
||||
if column, err := mreflect.GetGormColumnFromJsonField(field, reflect.TypeOf(t)); err == nil {
|
||||
return table + "." + column, nil
|
||||
}
|
||||
return "", fmt.Errorf(ERR, key, table)
|
||||
} else {
|
||||
table := mreflect.GetTableName[DEFAULT_TABLE_MODEL]()
|
||||
if column, err := mreflect.GetGormColumnFromJsonField(key, reflect.TypeOf(t)); err == nil {
|
||||
return table + "." + column, nil
|
||||
} else {
|
||||
return "", fmt.Errorf(ERR, key, table)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf(ERR, key, mreflect.GetTableName[DEFAULT_TABLE_MODEL]())
|
||||
}
|
||||
63
app/utils/query/query_params/params_query.go
Normal file
63
app/utils/query/query_params/params_query.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package query_params
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
var FunctionalQueryParams = []string{
|
||||
// Used to specidy order of results
|
||||
"sort",
|
||||
// Used to specify page of search resulst
|
||||
"p",
|
||||
// Used to specify number of elements on a page
|
||||
"elems",
|
||||
// Used to specify allowed values of features on products
|
||||
"values",
|
||||
}
|
||||
|
||||
func ParseFilters[T any](c fiber.Ctx, formColumnMappimg ...map[string]string) (find.Paging, *filters.FiltersList, error) {
|
||||
// field/column based filters
|
||||
filters, err := ParseFieldFilters[T](c, formColumnMappimg...)
|
||||
if err != nil {
|
||||
return find.Paging{}, filters, err
|
||||
}
|
||||
// pagination
|
||||
pageNum, pageSize := ParsePagination(c)
|
||||
|
||||
// ret
|
||||
return find.Paging{Page: pageNum, Elements: pageSize}, filters, nil
|
||||
}
|
||||
|
||||
// Parse field related filters from params query. Produces where clauses and
|
||||
// order rules.
|
||||
func ParseFieldFilters[T any](c fiber.Ctx, formColumnMapping ...map[string]string) (*filters.FiltersList, error) {
|
||||
// var model T
|
||||
list := filters.NewFiltersList()
|
||||
|
||||
whereScopefilters := ParseWhereScopes[T](c, []string{}, formColumnMapping...)
|
||||
list.Append(whereScopefilters...)
|
||||
|
||||
ord, err := ParseOrdering[T](c, formColumnMapping...)
|
||||
if err != nil {
|
||||
return &list, err
|
||||
}
|
||||
// addDefaultOrderingIfNeeded(&ord, model)
|
||||
for i := range ord {
|
||||
if err == nil {
|
||||
list.Append(filters.Order(ord[i].Column, ord[i].IsDesc))
|
||||
}
|
||||
}
|
||||
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
// TODO: Add some source of defaults for pagination size here
|
||||
func ParsePagination(c fiber.Ctx) (uint, uint) {
|
||||
pageNum, _ := strconv.ParseInt(c.Query("p", "1"), 10, 64)
|
||||
pageSize, _ := strconv.ParseInt(c.Query("elems", "30"), 10, 64)
|
||||
return uint(pageNum), uint(pageSize)
|
||||
}
|
||||
82
app/utils/query/query_params/parse_sort.go
Normal file
82
app/utils/query/query_params/parse_sort.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package query_params
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type Ordering struct {
|
||||
Column string
|
||||
IsDesc bool
|
||||
}
|
||||
|
||||
func ParseOrdering[T any](c fiber.Ctx, columnMapping ...map[string]string) ([]Ordering, error) {
|
||||
param := c.Query("sort")
|
||||
if len(param) < 1 {
|
||||
return []Ordering{}, nil
|
||||
}
|
||||
|
||||
rules := strings.Split(param, ";")
|
||||
var orderings []Ordering
|
||||
for _, r := range rules {
|
||||
ord, err := parseOrderingRule[T](r, columnMapping...)
|
||||
if err != nil {
|
||||
return orderings, err
|
||||
}
|
||||
orderings = append(orderings, ord)
|
||||
}
|
||||
return orderings, nil
|
||||
}
|
||||
|
||||
func parseOrderingRule[T any](rule string, columnMapping ...map[string]string) (Ordering, error) {
|
||||
var desc bool
|
||||
if key, descStr, ok := strings.Cut(rule, ","); ok {
|
||||
switch {
|
||||
case strings.Compare(descStr, "desc") == 0:
|
||||
desc = true
|
||||
case strings.Compare(descStr, "asc") == 0:
|
||||
desc = false
|
||||
default:
|
||||
desc = true
|
||||
}
|
||||
if col, err := MapParamsKeyToDbColumn[T](key, columnMapping...); err == nil {
|
||||
return Ordering{
|
||||
Column: col,
|
||||
IsDesc: desc,
|
||||
}, nil
|
||||
} else {
|
||||
return Ordering{}, err
|
||||
}
|
||||
} else {
|
||||
if col, err := MapParamsKeyToDbColumn[T](key, columnMapping...); err == nil {
|
||||
return Ordering{
|
||||
Column: col,
|
||||
IsDesc: true,
|
||||
}, nil
|
||||
} else {
|
||||
return Ordering{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func addDefaultOrderingIfNeeded[T any](previousOrderings *[]Ordering, model T) {
|
||||
// newOrderings := new([]Ordering)
|
||||
// var t T
|
||||
// if len(*previousOrderings) < 1 {
|
||||
// if col, err := mreflect.GetGormColumnFromJsonField("id", reflect.TypeOf(t)); err == nil {
|
||||
// *newOrderings = append(*newOrderings, Ordering{
|
||||
// Column: mreflect.GetTableName[T]() + "." + col,
|
||||
// IsDesc: true,
|
||||
// })
|
||||
// }
|
||||
// if col, err := mreflect.GetGormColumnFromJsonField("iso_code", reflect.TypeOf(t)); err == nil {
|
||||
// *newOrderings = append(*newOrderings, Ordering{
|
||||
// Column: mreflect.GetTableName[T]() + "." + col,
|
||||
// IsDesc: false,
|
||||
// })
|
||||
// }
|
||||
// *newOrderings = append(*newOrderings, *previousOrderings...)
|
||||
// *previousOrderings = *newOrderings
|
||||
// }
|
||||
// }
|
||||
75
app/utils/query/query_params/where_scope_from_query.go
Normal file
75
app/utils/query/query_params/where_scope_from_query.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package query_params
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
// ParseWhereScopes will attempt to create where scope query filters from url
|
||||
// query params. It will map form fields to a database column name using
|
||||
// `MapParamsKeyToDbColumn` function.
|
||||
func ParseWhereScopes[T any](c fiber.Ctx, ignoredKeys []string, formColumnMapping ...map[string]string) []filters.Filter {
|
||||
var parsedFilters []filters.Filter
|
||||
//nolint
|
||||
for key, value := range c.Request().URI().QueryArgs().All() {
|
||||
keyStr := string(key)
|
||||
valStr := string(value)
|
||||
|
||||
isIgnored := false
|
||||
for _, ignoredKey := range ignoredKeys {
|
||||
if keyStr == ignoredKey {
|
||||
isIgnored = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isIgnored {
|
||||
continue
|
||||
}
|
||||
|
||||
baseKey, operator := extractOperator(keyStr)
|
||||
|
||||
if col, err := MapParamsKeyToDbColumn[T](baseKey, formColumnMapping...); err == nil {
|
||||
if strings.HasPrefix(valStr, "~") {
|
||||
parsedFilters = append(parsedFilters, filters.WhereFromStrings(col, "LIKE", valStr))
|
||||
continue
|
||||
}
|
||||
|
||||
op := resolveOperator(operator)
|
||||
|
||||
parsedFilters = append(parsedFilters, filters.WhereFromStrings(col, op, valStr))
|
||||
}
|
||||
}
|
||||
|
||||
return parsedFilters
|
||||
}
|
||||
|
||||
func extractOperator(key string) (base string, operatorSuffix string) {
|
||||
suffixes := []string{"_gt", "_gte", "_lt", "_lte", "_eq", "_neq"}
|
||||
for _, suf := range suffixes {
|
||||
if strings.HasSuffix(key, suf) {
|
||||
return strings.TrimSuffix(key, suf), suf[1:]
|
||||
}
|
||||
}
|
||||
return key, ""
|
||||
}
|
||||
|
||||
func resolveOperator(suffix string) string {
|
||||
switch suffix {
|
||||
case "gt":
|
||||
return ">"
|
||||
case "gte":
|
||||
return ">="
|
||||
case "lt":
|
||||
return "<"
|
||||
case "lte":
|
||||
return "<="
|
||||
case "neq":
|
||||
return "!="
|
||||
case "eq":
|
||||
return "="
|
||||
default:
|
||||
return "LIKE"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user