-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathquery.go
137 lines (115 loc) · 3.58 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package filterparams
import (
"regexp"
"net/url"
"github.com/cbrand/go-filterparams/definition"
)
const defaultOperation = "eq"
var paramFilter = regexp.MustCompile("([a-zA-Z1-9_\\-]+)\\[([a-zA-Z1-9_\\-]+)\\](.*)")
var fieldFilter = regexp.MustCompile("\\[([a-zA-Z1-9_\\-]+)\\](.*)")
// Query can be used to parse query values.
type Query struct {
filters []*definition.Filter
defaultOperation string
}
// parseFilterArguments takes the filter arugments and parses the data.
func (q *Query) parseFilterArguments(values *url.Values) (*ValueFilterArguments, error) {
arguments := NewValueFilterArgument()
for param, valueList := range *values {
matches := paramFilter.FindStringSubmatch(param)
if len(matches) == 0 {
continue
}
category, innerCategory, remaining := matches[1], matches[2], matches[3]
if category != "filter" {
continue
}
for _, value := range valueList {
matches = fieldFilter.FindStringSubmatch(remaining)
if innerCategory == "param" {
parameter, err := q.parseFilterParam(matches[1], matches[2], value)
if err != nil {
return nil, err
}
arguments.SetArgument(parameter.Identification, parameter)
} else if (innerCategory == "binding") {
arguments.SetQueryBinding(value)
} else if (innerCategory == "order") {
arguments.AddOrder(value)
}
}
}
return arguments, nil
}
// parseFilterParam takes the basic configuration and generates a filter parameter.
func (q *Query) parseFilterParam(paramName, remainingKeyData, value string) (*definition.Parameter, error) {
remainingDataMatches := fieldFilter.FindStringSubmatch(remainingKeyData)
operation := defaultOperation
possibleRemainingAlias := ""
if remainingDataMatches != nil {
operation = remainingDataMatches[1]
possibleRemainingAlias = remainingDataMatches[2]
}
remainingAliasMatches := fieldFilter.FindStringSubmatch(possibleRemainingAlias)
alias := paramName
if remainingAliasMatches != nil {
alias = remainingAliasMatches[1]
}
parameter := definition.NewParameter(alias)
parameter.Name = paramName
parameter.Filter = q.getFilter(operation)
parameter.Value = value
if parameter.Filter == nil {
return nil, NewUnsupportedOperation(operation)
}
return parameter, nil
}
// getFilter returns the registered filter with the given name.
func (q *Query) getFilter(name string) *definition.Filter {
for _, filter := range q.filters {
if filter.Identification == name {
return filter
}
}
return nil
}
// GetDefaultOperation returns the default operation as the
// entry.
func (q *Query) GetDefaultOperation() string {
operation := defaultOperation
if len(q.defaultOperation) > 0 {
operation = q.defaultOperation
}
return operation
}
// setDefaultOperation is used by the builder to be able to
// configure a default operation.
func (q *Query) setDefaultOperation(operation string) {
q.defaultOperation = operation
}
// Parse takes the given values and returns the parsed data which is provided
// by the Go struct.
func (q *Query) Parse(values *url.Values) (*QueryData, error) {
arguments, err := q.parseFilterArguments(values)
if err != nil {
return nil, err
}
if !arguments.HasQueryBinding() {
arguments.SetQueryBinding(arguments.ConstructDefaultQueryBinding())
}
var binding interface{}
if len(arguments.arguments) > 0 {
binding, err = arguments.ParsedBinding()
if err != nil {
return nil, err
}
}
orders := arguments.ApplyOrders()
return NewQueryData(binding, orders), nil
}
// newQuery uses the QueryBuilder to create a new Query entry.
func newQuery(allowedFilters []*definition.Filter) *Query {
return &Query{
filters: allowedFilters,
}
}